Stream - Version 3.0.5

Version Description

  • March 15, 2015 =

  • New: Export your Stream records as CSV or JSON. (#823)

  • Tweak: More mobile responsive list table (#810)

  • Tweak: Better Javascript conflict prevention (#812)

  • Tweak: Minor styling updates. It's about attention to detail. (#826)

  • Fix: Gravity Forms error when adding a note (#811)

  • Fix: In some instances, custom roles weren't being logged by Stream (#824)

  • Fix: The Customiser fix you've been waiting for! Stream now properly records changes made from the Customiser. (#827)

Props @chacha, @lukecarbis, @Stayallive, @barryceelen, Jonathan Desrosiers, @marcin-lawrowski

Download this release

Release Info

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

Code changes from version 3.0.4 to 3.0.5

classes/class-admin.php CHANGED
@@ -10,20 +10,32 @@ use \WP_Roles;
10
  class Admin {
11
  /**
12
  * Hold Plugin class
 
13
  * @var Plugin
14
  */
15
  public $plugin;
16
 
17
  /**
 
 
18
  * @var Network
19
  */
20
  public $network;
21
 
22
  /**
 
 
23
  * @var Live_Update
24
  */
25
  public $live_update;
26
 
 
 
 
 
 
 
 
27
  /**
28
  * Menu page screen id
29
  *
@@ -111,12 +123,12 @@ class Admin {
111
 
112
  add_action( 'init', array( $this, 'init' ) );
113
 
114
- // Ensure function used in various methods is pre-loaded
115
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
116
  require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
117
  }
118
 
119
- // User and role caps
120
  add_filter( 'user_has_cap', array( $this, 'filter_user_caps' ), 10, 4 );
121
  add_filter( 'role_has_cap', array( $this, 'filter_role_caps' ), 10, 3 );
122
 
@@ -127,46 +139,46 @@ class Admin {
127
  $this->disable_access = ( $option ) ? false : true;
128
  }
129
 
130
- // Register settings page
131
  if ( ! $this->disable_access ) {
132
  add_action( 'admin_menu', array( $this, 'register_menu' ) );
133
  }
134
 
135
- // Admin notices
136
  add_action( 'admin_notices', array( $this, 'prepare_admin_notices' ) );
137
  add_action( 'shutdown', array( $this, 'admin_notices' ) );
138
 
139
- // Add admin body class
140
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
141
 
142
- // Plugin action links
143
  add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
144
 
145
- // Load admin scripts and styles
146
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
147
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
148
 
149
- // Reset Streams database
150
  add_action( 'wp_ajax_wp_stream_reset', array( $this, 'wp_ajax_reset' ) );
151
 
152
- // Uninstall Streams and Deactivate plugin
153
  $uninstall = new Uninstall( $this->plugin );
154
  add_action( 'wp_ajax_wp_stream_uninstall', array( $uninstall, 'uninstall' ) );
155
 
156
- // Auto purge setup
157
  add_action( 'wp_loaded', array( $this, 'purge_schedule_setup' ) );
158
  add_action( 'wp_stream_auto_purge', array( $this, 'purge_scheduled_action' ) );
159
 
160
- // Ajax users list
161
  add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
162
 
163
- // Ajax user's name by ID
164
  add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
165
 
166
- // Ajax users list
167
  add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
168
 
169
- // Ajax user's name by ID
170
  add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
171
  }
172
 
@@ -178,14 +190,15 @@ class Admin {
178
  public function init() {
179
  $this->network = new Network( $this->plugin );
180
  $this->live_update = new Live_Update( $this->plugin );
 
181
  }
182
 
183
  /**
184
- * Output specific updates passed as URL parameters
185
  *
186
  * @action admin_notices
187
  *
188
- * @return string
189
  */
190
  public function prepare_admin_notices() {
191
  $message = wp_stream_filter_input( INPUT_GET, 'message' );
@@ -200,8 +213,8 @@ class Admin {
200
  /**
201
  * Handle notice messages according to the appropriate context (WP-CLI or the WP Admin)
202
  *
203
- * @param string $message
204
- * @param bool $is_error
205
  */
206
  public function notice( $message, $is_error = true ) {
207
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
@@ -213,12 +226,12 @@ class Admin {
213
  WP_CLI::success( $message );
214
  }
215
  } else {
216
- // Trigger admin notices late, so that any notices which occur during page load are displayed
217
  add_action( 'shutdown', array( $this, 'admin_notices' ) );
218
 
219
  $notice = compact( 'message', 'is_error' );
220
 
221
- if ( ! in_array( $notice, $this->notices ) ) {
222
  $this->notices[] = $notice;
223
  }
224
  }
@@ -259,7 +272,7 @@ class Admin {
259
  *
260
  * @action admin_menu
261
  *
262
- * @return bool|void
263
  */
264
  public function register_menu() {
265
  /**
@@ -319,7 +332,7 @@ class Admin {
319
  */
320
  do_action( 'wp_stream_admin_menu_screens' );
321
 
322
- // Register the list table early, so it associates the column headers with 'Screen settings'
323
  add_action( 'load-' . $this->screen_id['main'], array( $this, 'register_list_table' ) );
324
  }
325
  }
@@ -334,31 +347,31 @@ class Admin {
334
  * @return void
335
  */
336
  public function admin_enqueue_scripts( $hook ) {
337
- wp_register_script( 'select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.js', array( 'jquery' ), '3.5.2', true );
338
- wp_register_style( 'select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.css', array(), '3.5.2' );
339
- wp_register_script( 'timeago', $this->plugin->locations['url'] . 'ui/lib/timeago/jquery.timeago.js', array(), '1.4.1', true );
340
 
341
  $locale = strtolower( substr( get_locale(), 0, 2 ) );
342
  $file_tmpl = 'ui/lib/timeago/locales/jquery.timeago.%s.js';
343
 
344
  if ( file_exists( $this->plugin->locations['dir'] . sprintf( $file_tmpl, $locale ) ) ) {
345
- wp_register_script( 'timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, $locale ), array( 'timeago' ), '1' );
346
  } else {
347
- wp_register_script( 'timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'timeago' ), '1' );
348
  }
349
 
350
  wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.css', array(), $this->plugin->get_version() );
351
 
352
  $script_screens = array( 'plugins.php' );
353
 
354
- if ( in_array( $hook, $this->screen_id ) || in_array( $hook, $script_screens ) ) {
355
- wp_enqueue_script( 'select2' );
356
- wp_enqueue_style( 'select2' );
357
 
358
- wp_enqueue_script( 'timeago' );
359
- wp_enqueue_script( 'timeago-locale' );
360
 
361
- wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'select2' ), $this->plugin->get_version() );
362
  wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
363
 
364
  wp_localize_script(
@@ -434,7 +447,7 @@ class Admin {
434
  /**
435
  * Add a specific body class to all Stream admin screens
436
  *
437
- * @param string $classes
438
  *
439
  * @filter admin_body_class
440
  *
@@ -762,7 +775,7 @@ class Admin {
762
  * @return bool
763
  */
764
  private function role_can_view( $role ) {
765
- if ( in_array( $role, $this->plugin->settings->options['general_role_access'] ) ) {
766
  return true;
767
  }
768
 
@@ -803,7 +816,7 @@ class Admin {
803
  $stream_view_caps = array( $this->view_cap );
804
 
805
  foreach ( $caps as $cap ) {
806
- if ( in_array( $cap, $stream_view_caps ) ) {
807
  foreach ( $roles as $role ) {
808
  if ( $this->role_can_view( $role ) ) {
809
  $allcaps[ $cap ] = true;
@@ -831,7 +844,7 @@ class Admin {
831
  public function filter_role_caps( $allcaps, $cap, $role ) {
832
  $stream_view_caps = array( $this->view_cap );
833
 
834
- if ( in_array( $cap, $stream_view_caps ) && $this->role_can_view( $role ) ) {
835
  $allcaps[ $cap ] = true;
836
  }
837
 
10
  class Admin {
11
  /**
12
  * Hold Plugin class
13
+ *
14
  * @var Plugin
15
  */
16
  public $plugin;
17
 
18
  /**
19
+ * Holds Network class
20
+ *
21
  * @var Network
22
  */
23
  public $network;
24
 
25
  /**
26
+ * Holds Live Update class
27
+ *
28
  * @var Live_Update
29
  */
30
  public $live_update;
31
 
32
+ /**
33
+ * Holds Export class
34
+ *
35
+ * @var Export
36
+ */
37
+ public $export;
38
+
39
  /**
40
  * Menu page screen id
41
  *
123
 
124
  add_action( 'init', array( $this, 'init' ) );
125
 
126
+ // Ensure function used in various methods is pre-loaded.
127
  if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
128
  require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
129
  }
130
 
131
+ // User and role caps.
132
  add_filter( 'user_has_cap', array( $this, 'filter_user_caps' ), 10, 4 );
133
  add_filter( 'role_has_cap', array( $this, 'filter_role_caps' ), 10, 3 );
134
 
139
  $this->disable_access = ( $option ) ? false : true;
140
  }
141
 
142
+ // Register settings page.
143
  if ( ! $this->disable_access ) {
144
  add_action( 'admin_menu', array( $this, 'register_menu' ) );
145
  }
146
 
147
+ // Admin notices.
148
  add_action( 'admin_notices', array( $this, 'prepare_admin_notices' ) );
149
  add_action( 'shutdown', array( $this, 'admin_notices' ) );
150
 
151
+ // Add admin body class.
152
  add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
153
 
154
+ // Plugin action links.
155
  add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
156
 
157
+ // Load admin scripts and styles.
158
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
159
  add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
160
 
161
+ // Reset Streams database.
162
  add_action( 'wp_ajax_wp_stream_reset', array( $this, 'wp_ajax_reset' ) );
163
 
164
+ // Uninstall Streams and Deactivate plugin.
165
  $uninstall = new Uninstall( $this->plugin );
166
  add_action( 'wp_ajax_wp_stream_uninstall', array( $uninstall, 'uninstall' ) );
167
 
168
+ // Auto purge setup.
169
  add_action( 'wp_loaded', array( $this, 'purge_schedule_setup' ) );
170
  add_action( 'wp_stream_auto_purge', array( $this, 'purge_scheduled_action' ) );
171
 
172
+ // Ajax users list.
173
  add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
174
 
175
+ // Ajax user's name by ID.
176
  add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
177
 
178
+ // Ajax users list.
179
  add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
180
 
181
+ // Ajax user's name by ID.
182
  add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
183
  }
184
 
190
  public function init() {
191
  $this->network = new Network( $this->plugin );
192
  $this->live_update = new Live_Update( $this->plugin );
193
+ $this->export = new Export( $this->plugin );
194
  }
195
 
196
  /**
197
+ * Output specific updates passed as URL parameters.
198
  *
199
  * @action admin_notices
200
  *
201
+ * @return void
202
  */
203
  public function prepare_admin_notices() {
204
  $message = wp_stream_filter_input( INPUT_GET, 'message' );
213
  /**
214
  * Handle notice messages according to the appropriate context (WP-CLI or the WP Admin)
215
  *
216
+ * @param string $message Message to output.
217
+ * @param bool $is_error If the message is error_level (true) or warning (false).
218
  */
219
  public function notice( $message, $is_error = true ) {
220
  if ( defined( 'WP_CLI' ) && WP_CLI ) {
226
  WP_CLI::success( $message );
227
  }
228
  } else {
229
+ // Trigger admin notices late, so that any notices which occur during page load are displayed.
230
  add_action( 'shutdown', array( $this, 'admin_notices' ) );
231
 
232
  $notice = compact( 'message', 'is_error' );
233
 
234
+ if ( ! in_array( $notice, $this->notices, true ) ) {
235
  $this->notices[] = $notice;
236
  }
237
  }
272
  *
273
  * @action admin_menu
274
  *
275
+ * @return void
276
  */
277
  public function register_menu() {
278
  /**
332
  */
333
  do_action( 'wp_stream_admin_menu_screens' );
334
 
335
+ // Register the list table early, so it associates the column headers with 'Screen settings'.
336
  add_action( 'load-' . $this->screen_id['main'], array( $this, 'register_list_table' ) );
337
  }
338
  }
347
  * @return void
348
  */
349
  public function admin_enqueue_scripts( $hook ) {
350
+ wp_register_script( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.js', array( 'jquery' ), '3.5.2', true );
351
+ wp_register_style( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.css', array(), '3.5.2' );
352
+ wp_register_script( 'wp-stream-timeago', $this->plugin->locations['url'] . 'ui/lib/timeago/jquery.timeago.js', array(), '1.4.1', true );
353
 
354
  $locale = strtolower( substr( get_locale(), 0, 2 ) );
355
  $file_tmpl = 'ui/lib/timeago/locales/jquery.timeago.%s.js';
356
 
357
  if ( file_exists( $this->plugin->locations['dir'] . sprintf( $file_tmpl, $locale ) ) ) {
358
+ wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, $locale ), array( 'wp-stream-timeago' ), '1' );
359
  } else {
360
+ wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'wp-stream-timeago' ), '1' );
361
  }
362
 
363
  wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.css', array(), $this->plugin->get_version() );
364
 
365
  $script_screens = array( 'plugins.php' );
366
 
367
+ if ( in_array( $hook, $this->screen_id, true ) || in_array( $hook, $script_screens, true ) ) {
368
+ wp_enqueue_script( 'wp-stream-select2' );
369
+ wp_enqueue_style( 'wp-stream-select2' );
370
 
371
+ wp_enqueue_script( 'wp-stream-timeago' );
372
+ wp_enqueue_script( 'wp-stream-timeago-locale' );
373
 
374
+ wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
375
  wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
376
 
377
  wp_localize_script(
447
  /**
448
  * Add a specific body class to all Stream admin screens
449
  *
450
+ * @param string $classes CSS classes to output to body
451
  *
452
  * @filter admin_body_class
453
  *
775
  * @return bool
776
  */
777
  private function role_can_view( $role ) {
778
+ if ( in_array( $role, $this->plugin->settings->options['general_role_access'], true ) ) {
779
  return true;
780
  }
781
 
816
  $stream_view_caps = array( $this->view_cap );
817
 
818
  foreach ( $caps as $cap ) {
819
+ if ( in_array( $cap, $stream_view_caps, true ) ) {
820
  foreach ( $roles as $role ) {
821
  if ( $this->role_can_view( $role ) ) {
822
  $allcaps[ $cap ] = true;
844
  public function filter_role_caps( $allcaps, $cap, $role ) {
845
  $stream_view_caps = array( $this->view_cap );
846
 
847
+ if ( in_array( $cap, $stream_view_caps, true ) && $this->role_can_view( $role ) ) {
848
  $allcaps[ $cap ] = true;
849
  }
850
 
classes/class-connector.php CHANGED
@@ -35,7 +35,7 @@ abstract class Connector {
35
  */
36
  public function register() {
37
  foreach ( $this->actions as $action ) {
38
- add_action( $action, array( $this, 'callback' ), 10, 5 );
39
  }
40
 
41
  add_filter( 'wp_stream_action_links_' . $this->name, array( $this, 'action_links' ), 10, 2 );
@@ -200,9 +200,9 @@ abstract class Connector {
200
  $result = array_fill_keys( $result, null );
201
 
202
  foreach ( $result as $key => $val ) {
203
- if ( in_array( $key, $unique_keys_old ) ) {
204
  $result[ $key ] = false; // Removed
205
- } elseif ( in_array( $key, $unique_keys_new ) ) {
206
  $result[ $key ] = true; // Added
207
  } elseif ( $deep ) { // Changed, find what changed, only if we're allowed to explore a new level
208
  if ( is_array( $old_value[ $key ] ) && is_array( $new_value[ $key ] ) ) {
35
  */
36
  public function register() {
37
  foreach ( $this->actions as $action ) {
38
+ add_action( $action, array( $this, 'callback' ), 10, 99 );
39
  }
40
 
41
  add_filter( 'wp_stream_action_links_' . $this->name, array( $this, 'action_links' ), 10, 2 );
200
  $result = array_fill_keys( $result, null );
201
 
202
  foreach ( $result as $key => $val ) {
203
+ if ( in_array( $key, $unique_keys_old, true ) ) {
204
  $result[ $key ] = false; // Removed
205
+ } elseif ( in_array( $key, $unique_keys_new, true ) ) {
206
  $result[ $key ] = true; // Added
207
  } elseif ( $deep ) { // Changed, find what changed, only if we're allowed to explore a new level
208
  if ( is_array( $old_value[ $key ] ) && is_array( $new_value[ $key ] ) ) {
classes/class-connectors.php CHANGED
@@ -97,10 +97,6 @@ class Connectors {
97
  }
98
  }
99
 
100
- if ( empty( $classes ) ) {
101
- return;
102
- }
103
-
104
  /**
105
  * Allows for adding additional connectors via classes that extend Connector.
106
  *
@@ -108,6 +104,10 @@ class Connectors {
108
  */
109
  $this->connectors = apply_filters( 'wp_stream_connectors', $classes );
110
 
 
 
 
 
111
  foreach ( $this->connectors as $connector ) {
112
  if ( ! method_exists( $connector, 'get_label' ) ) {
113
  continue;
@@ -143,12 +143,12 @@ class Connectors {
143
  }
144
 
145
  // Store connector label
146
- if ( ! in_array( $connector->name, $this->term_labels['stream_connector'] ) ) {
147
  $this->term_labels['stream_connector'][ $connector->name ] = $connector->get_label();
148
  }
149
 
150
  $connector_name = $connector->name;
151
- $is_excluded = in_array( $connector_name, $excluded_connectors );
152
 
153
  /**
154
  * Allows excluded connectors to be overridden and registered.
97
  }
98
  }
99
 
 
 
 
 
100
  /**
101
  * Allows for adding additional connectors via classes that extend Connector.
102
  *
104
  */
105
  $this->connectors = apply_filters( 'wp_stream_connectors', $classes );
106
 
107
+ if ( empty( $this->connectors ) ) {
108
+ return;
109
+ }
110
+
111
  foreach ( $this->connectors as $connector ) {
112
  if ( ! method_exists( $connector, 'get_label' ) ) {
113
  continue;
143
  }
144
 
145
  // Store connector label
146
+ if ( ! in_array( $connector->name, $this->term_labels['stream_connector'], true ) ) {
147
  $this->term_labels['stream_connector'][ $connector->name ] = $connector->get_label();
148
  }
149
 
150
  $connector_name = $connector->name;
151
+ $is_excluded = in_array( $connector_name, $excluded_connectors, true );
152
 
153
  /**
154
  * Allows excluded connectors to be overridden and registered.
classes/class-db.php CHANGED
@@ -182,7 +182,7 @@ class DB {
182
 
183
  // Sanitize column
184
  $allowed_columns = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
185
- if ( ! in_array( $column, $allowed_columns ) ) {
186
  return array();
187
  }
188
 
182
 
183
  // Sanitize column
184
  $allowed_columns = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
185
+ if ( ! in_array( $column, $allowed_columns, true ) ) {
186
  return array();
187
  }
188
 
classes/class-export.php ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Export {
5
+ /**
6
+ * Hold Plugin class
7
+ *
8
+ * @var Plugin
9
+ */
10
+ public $plugin;
11
+
12
+ /**
13
+ * Hold registered exporters
14
+ *
15
+ * @var array
16
+ */
17
+ protected $exporters = array();
18
+
19
+ /**
20
+ * Class constructor
21
+ *
22
+ * @param Plugin $plugin The plugin object.
23
+ * @return void
24
+ */
25
+ public function __construct( $plugin ) {
26
+ $this->plugin = $plugin;
27
+
28
+ if ( 'wp_stream' === wp_stream_filter_input( INPUT_GET, 'page' ) ) {
29
+ add_action( 'admin_init', array( $this, 'render_download' ) );
30
+ add_action( 'wp_stream_record_actions_menu', array( $this, 'actions_menu_export_items' ) );
31
+ $this->register_exporters();
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Outputs download file to user based on selected exporter
37
+ *
38
+ * @return void
39
+ */
40
+ public function render_download() {
41
+ $action = wp_stream_filter_input( INPUT_GET, 'record-actions' );
42
+ if ( strpos( $action, 'export-' ) !== 0 ) {
43
+ return;
44
+ }
45
+
46
+ $output_type = str_replace( 'export-', '', $action );
47
+ if ( ! array_key_exists( $output_type, $this->get_exporters() ) ) {
48
+ return;
49
+ }
50
+
51
+ $this->plugin->admin->register_list_table();
52
+ $list_table = $this->plugin->admin->list_table;
53
+ $list_table->prepare_items();
54
+ add_filter( 'stream_records_per_page', array( $this, 'disable_paginate' ) );
55
+ add_filter( 'wp_stream_list_table_columns', array( $this, 'expand_columns' ), 10, 1 );
56
+
57
+ $records = $list_table->get_records();
58
+ $columns = $list_table->get_columns();
59
+ $output = array();
60
+ foreach ( $records as $item ) {
61
+ $output[] = $this->build_record( $item, $columns );
62
+ }
63
+
64
+ $exporters = $this->get_exporters();
65
+ $exporter = $exporters[ $output_type ];
66
+ $exporter->output_file( $output, $columns );
67
+ return;
68
+ }
69
+
70
+ /**
71
+ * Add Export options to record actions menu
72
+ *
73
+ * @return array
74
+ */
75
+ function actions_menu_export_items( $action_menu_items ) {
76
+ foreach ( $this->get_exporters() as $exporter ) {
77
+ $action = 'export-' . $exporter->slug;
78
+ $action_menu_items[ $action ] = sprintf( __( 'Export as %s', 'stream' ), $exporter->name );
79
+ }
80
+
81
+ return $action_menu_items;
82
+ }
83
+
84
+ /**
85
+ * Extracts data from Records
86
+ *
87
+ * @param array $item Post to extract data from.
88
+ * @param array $columns Columns being extracted.
89
+ * @return array Numerically-indexed array with extracted data.
90
+ */
91
+ function build_record( $item, $columns ) {
92
+ $record = new Record( $item );
93
+
94
+ $row_out = array();
95
+ foreach ( array_keys( $columns ) as $column_name ) {
96
+ switch ( $column_name ) {
97
+ case 'date' :
98
+ $created = date( 'Y-m-d H:i:s', strtotime( $record->created ) );
99
+ $row_out[ $column_name ] = get_date_from_gmt( $created, 'Y/m/d h:i:s A' );
100
+ break;
101
+
102
+ case 'summary' :
103
+ $row_out[ $column_name ] = $record->summary;
104
+ break;
105
+
106
+ case 'user_id' :
107
+ $user = new Author( (int) $record->user_id, (array) maybe_unserialize( $record->user_meta ) );
108
+ $row_out[ $column_name ] = $user->get_display_name();
109
+ break;
110
+
111
+ case 'connector':
112
+ $row_out[ $column_name ] = $record->connector;
113
+ break;
114
+
115
+ case 'context':
116
+ $row_out[ $column_name ] = $record->context;
117
+ break;
118
+
119
+ case 'action':
120
+ $row_out[ $column_name ] = $record->{$column_name};
121
+ break;
122
+
123
+ case 'blog_id':
124
+ $row_out[ $column_name ] = $record->blog_id;
125
+ break;
126
+
127
+ case 'ip' :
128
+ $row_out[ $column_name ] = $record->{$column_name};
129
+ break;
130
+ }
131
+ }
132
+
133
+ return $row_out;
134
+ }
135
+
136
+ /**
137
+ * Increase pagination limit for CSV Output
138
+ *
139
+ * @param int $records_per_page Old limit for records_per_page.
140
+ */
141
+ public function disable_paginate( $records_per_page ) {
142
+ return 10000;
143
+ }
144
+
145
+ /**
146
+ * Expand columns for CSV Output
147
+ *
148
+ * @param array $columns Columns currently registered to the list table being exported.
149
+ * @return array New columns for exporting.
150
+ */
151
+ public function expand_columns( $columns ) {
152
+ $new_columns = array(
153
+ 'date' => $columns['date'],
154
+ 'summary' => $columns['summary'],
155
+ 'user_id' => $columns['user_id'],
156
+ 'connector' => __( 'Connector', 'stream' ),
157
+ 'context' => $columns['context'],
158
+ 'action' => $columns['action'],
159
+ 'ip' => $columns['ip'],
160
+ );
161
+
162
+ if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
163
+ $new_columns['blog_id'] = __( 'Blog ID', 'stream' );
164
+ }
165
+
166
+ return $new_columns;
167
+ }
168
+
169
+ /**
170
+ * Registers all available exporters
171
+ *
172
+ * @return void
173
+ */
174
+ public function register_exporters() {
175
+ $exporters = array(
176
+ 'csv',
177
+ 'json',
178
+ );
179
+
180
+ $classes = array();
181
+ foreach ( $exporters as $exporter ) {
182
+ include_once $this->plugin->locations['dir'] . '/exporters/class-exporter-' . $exporter .'.php';
183
+ $class_name = sprintf( '\WP_Stream\Exporter_%s', str_replace( '-', '_', $exporter ) );
184
+ if ( ! class_exists( $class_name ) ) {
185
+ continue;
186
+ }
187
+ $class = new $class_name();
188
+ if ( ! property_exists( $class, 'slug' ) ) {
189
+ continue;
190
+ }
191
+ $classes[ $class->slug ] = $class;
192
+ }
193
+
194
+ /**
195
+ * Allows for adding additional exporters via classes that extend Exporter.
196
+ *
197
+ * @param array $classes An array of Exporter objects. In the format exporter_slug => Exporter_Class()
198
+ */
199
+ $this->exporters = apply_filters( 'wp_stream_exporters', $classes );
200
+
201
+ // Ensure that all exporters extend Exporter
202
+ foreach ( $this->exporters as $key => $exporter ) {
203
+ if ( ! $this->is_valid_exporter( $exporter ) ) {
204
+ unset( $this->exporters[ $key ] );
205
+ trigger_error(
206
+ sprintf(
207
+ esc_html__( 'Registered exporter %s does not extend WP_Stream\Exporter.', 'stream' ),
208
+ esc_html( get_class( $exporter ) )
209
+ )
210
+ );
211
+ }
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Checks whether an exporter class is valid
217
+ *
218
+ * @param Exporter $exporter The class to check.
219
+ * @return bool
220
+ */
221
+ public function is_valid_exporter( $exporter ) {
222
+ if ( ! is_a( $exporter, 'WP_Stream\Exporter' ) ) {
223
+ return false;
224
+ }
225
+
226
+ if ( ! method_exists( $exporter, 'is_dependency_satisfied' ) || ! $exporter->is_dependency_satisfied() ) {
227
+ return false;
228
+ }
229
+
230
+ return true;
231
+ }
232
+
233
+
234
+ /**
235
+ * Returns an array with all available exporters
236
+ *
237
+ * @return array
238
+ */
239
+ public function get_exporters() {
240
+ return $this->exporters;
241
+ }
242
+ }
classes/class-exporter.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ abstract class Exporter {
5
+ /**
6
+ * Exporter name
7
+ *
8
+ * @var string
9
+ */
10
+ public $name;
11
+
12
+ /**
13
+ * Exporter slug
14
+ *
15
+ * @var string
16
+ */
17
+ public $slug;
18
+
19
+ /**
20
+ * Output formatted data for download
21
+ *
22
+ * @param array $data Array of data to output.
23
+ * @param array $columns Column names included in data set.
24
+ * @return void
25
+ */
26
+ public abstract function output_file( $data, $columns );
27
+
28
+ /**
29
+ * Allow connectors to determine if their dependencies is satisfied or not
30
+ *
31
+ * @return bool
32
+ */
33
+ public function is_dependency_satisfied() {
34
+ return true;
35
+ }
36
+ }
classes/class-list-table.php CHANGED
@@ -4,6 +4,7 @@ namespace WP_Stream;
4
  class List_Table extends \WP_List_Table {
5
  /**
6
  * Hold Plugin class
 
7
  * @var Plugin
8
  */
9
  public $plugin;
@@ -54,7 +55,10 @@ class List_Table extends \WP_List_Table {
54
 
55
  function extra_tablenav( $which ) {
56
  if ( 'top' === $which ) {
57
- echo $this->filters_form(); //xss ok
 
 
 
58
  }
59
  }
60
 
@@ -203,6 +207,7 @@ class List_Table extends \WP_List_Table {
203
  if ( ! isset( $args['records_per_page'] ) ) {
204
  $args['records_per_page'] = $this->get_items_per_page( 'edit_stream_per_page', 20 );
205
  }
 
206
 
207
  $items = $this->plugin->db->query( $args );
208
 
@@ -779,6 +784,39 @@ class List_Table extends \WP_List_Table {
779
  return ob_get_clean();
780
  }
781
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
782
  function display() {
783
  $url = self_admin_url( $this->plugin->admin->admin_parent_page );
784
 
4
  class List_Table extends \WP_List_Table {
5
  /**
6
  * Hold Plugin class
7
+ *
8
  * @var Plugin
9
  */
10
  public $plugin;
55
 
56
  function extra_tablenav( $which ) {
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
 
207
  if ( ! isset( $args['records_per_page'] ) ) {
208
  $args['records_per_page'] = $this->get_items_per_page( 'edit_stream_per_page', 20 );
209
  }
210
+ $args['records_per_page'] = apply_filters( 'stream_records_per_page', $args['records_per_page'] );
211
 
212
  $items = $this->plugin->db->query( $args );
213
 
784
  return ob_get_clean();
785
  }
786
 
787
+ /**
788
+ * Output a Select dropdown of actions relating to the Stream records
789
+ *
790
+ * @return string
791
+ */
792
+ function record_actions_form() {
793
+ /**
794
+ * Filter the records screen actions dropdown menu
795
+ *
796
+ * @return array Should be in the format of action_slug => 'Action Name'
797
+ */
798
+ $actions = apply_filters( 'wp_stream_record_actions_menu', array() );
799
+
800
+ if ( empty( $actions ) ) {
801
+ return '';
802
+ }
803
+
804
+ ob_start();
805
+ printf( '<div class="alignleft actions recordactions"><select name="%s">', esc_attr( 'record-actions' ) );
806
+ printf( '<option value="">%s</option>', esc_attr__( 'Record Actions', 'stream' ) );
807
+ foreach ( $actions as $value => $name ) {
808
+ printf(
809
+ '<option value="%s">%s</option>',
810
+ esc_attr( $value ),
811
+ esc_attr( $name )
812
+ );
813
+ }
814
+ echo '</select></div>';
815
+ printf( '<input type="submit" name="" id="record-actions-submit" class="button" value="%s">', esc_attr__( 'Apply', 'stream' ) );
816
+
817
+ return ob_get_clean();
818
+ }
819
+
820
  function display() {
821
  $url = self_admin_url( $this->plugin->admin->admin_parent_page );
822
 
classes/class-log.php CHANGED
@@ -104,12 +104,18 @@ class Log {
104
  // Get the current time in milliseconds
105
  $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
106
 
 
 
 
 
 
 
107
  $recordarr = array(
108
  'object_id' => (int) $object_id,
109
  'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
110
  'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
111
  'user_id' => (int) $user_id,
112
- 'user_role' => (string) ! empty( $user->roles ) ? $user->roles[0] : '',
113
  'created' => (string) $iso_8601_extended_date,
114
  'summary' => (string) vsprintf( $message, $args ),
115
  'connector' => (string) $connector,
@@ -152,14 +158,18 @@ class Log {
152
  $ip = wp_stream_filter_var( $ip, FILTER_VALIDATE_IP );
153
  }
154
 
155
- $user_role = isset( $user->roles[0] ) ? $user->roles[0] : null;
156
-
 
 
 
 
157
  $record = array(
158
  'connector' => $connector,
159
  'context' => $context,
160
  'action' => $action,
161
  'author' => $user->ID,
162
- 'role' => $user_role,
163
  'ip_address' => $ip,
164
  );
165
 
104
  // Get the current time in milliseconds
105
  $iso_8601_extended_date = wp_stream_get_iso_8601_extended_date();
106
 
107
+ if ( ! empty( $user->roles ) ) {
108
+ $roles = array_values( $user->roles );
109
+ $role = $roles[0];
110
+ } else {
111
+ $role = '';
112
+ }
113
  $recordarr = array(
114
  'object_id' => (int) $object_id,
115
  'site_id' => (int) is_multisite() ? get_current_site()->id : 1,
116
  'blog_id' => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
117
  'user_id' => (int) $user_id,
118
+ 'user_role' => (string) $role,
119
  'created' => (string) $iso_8601_extended_date,
120
  'summary' => (string) vsprintf( $message, $args ),
121
  'connector' => (string) $connector,
158
  $ip = wp_stream_filter_var( $ip, FILTER_VALIDATE_IP );
159
  }
160
 
161
+ if ( ! empty( $user->roles ) ) {
162
+ $roles = array_values( $user->roles );
163
+ $role = $roles[0];
164
+ } else {
165
+ $role = '';
166
+ }
167
  $record = array(
168
  'connector' => $connector,
169
  'context' => $context,
170
  'action' => $action,
171
  'author' => $user->ID,
172
+ 'role' => $role,
173
  'ip_address' => $ip,
174
  );
175
 
classes/class-network.php CHANGED
@@ -257,7 +257,7 @@ class Network {
257
  continue;
258
  }
259
 
260
- if ( in_array( $field['name'], $hidden_options[ $section_key ] ) ) {
261
  unset( $fields[ $section_key ]['fields'][ $key ] );
262
  }
263
  }
@@ -319,7 +319,7 @@ class Network {
319
  $this->default_settings_page_slug,
320
  );
321
 
322
- if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referers ) ) {
323
  return;
324
  }
325
 
257
  continue;
258
  }
259
 
260
+ if ( in_array( $field['name'], $hidden_options[ $section_key ], true ) ) {
261
  unset( $fields[ $section_key ]['fields'][ $key ] );
262
  }
263
  }
319
  $this->default_settings_page_slug,
320
  );
321
 
322
+ if ( ! isset( $_GET['action'] ) || ! in_array( $_GET['action'], $allowed_referers, true ) ) {
323
  return;
324
  }
325
 
classes/class-plugin.php CHANGED
@@ -7,7 +7,7 @@ class Plugin {
7
  *
8
  * @const string
9
  */
10
- const VERSION = '3.0.4';
11
 
12
  /**
13
  * WP-CLI command
7
  *
8
  * @const string
9
  */
10
+ const VERSION = '3.0.5';
11
 
12
  /**
13
  * WP-CLI command
classes/class-query.php CHANGED
@@ -128,7 +128,7 @@ class Query {
128
 
129
  // Sanitize field
130
  $allowed_fields = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
131
- if ( in_array( $field, $allowed_fields ) ) {
132
  $where .= $wpdb->prepare( " AND $wpdb->stream.{$field} LIKE %s", "%{$args['search']}%" ); // @codingStandardsIgnoreLine can't prepare column name
133
  }
134
  }
@@ -252,7 +252,7 @@ class Query {
252
  $orderby = esc_sql( $args['orderby'] );
253
  $orderable = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'summary', 'created', 'connector', 'context', 'action' );
254
 
255
- if ( in_array( $orderby, $orderable ) ) {
256
  $orderby = sprintf( '%s.%s', $wpdb->stream, $orderby );
257
  } elseif ( 'meta_value_num' === $orderby && ! empty( $args['meta_key'] ) ) {
258
  $orderby = "CAST($wpdb->streammeta.meta_value AS SIGNED)";
@@ -314,7 +314,7 @@ class Query {
314
  $this->found_records = absint( $wpdb->get_var( 'SELECT FOUND_ROWS()' ) );
315
 
316
  // Add meta to the records, when applicable
317
- if ( empty( $fields ) || in_array( 'meta', $fields ) ) {
318
  $results = $this->add_record_meta( $results );
319
  }
320
 
128
 
129
  // Sanitize field
130
  $allowed_fields = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'created', 'summary', 'connector', 'context', 'action', 'ip' );
131
+ if ( in_array( $field, $allowed_fields, true ) ) {
132
  $where .= $wpdb->prepare( " AND $wpdb->stream.{$field} LIKE %s", "%{$args['search']}%" ); // @codingStandardsIgnoreLine can't prepare column name
133
  }
134
  }
252
  $orderby = esc_sql( $args['orderby'] );
253
  $orderable = array( 'ID', 'site_id', 'blog_id', 'object_id', 'user_id', 'user_role', 'summary', 'created', 'connector', 'context', 'action' );
254
 
255
+ if ( in_array( $orderby, $orderable, true ) ) {
256
  $orderby = sprintf( '%s.%s', $wpdb->stream, $orderby );
257
  } elseif ( 'meta_value_num' === $orderby && ! empty( $args['meta_key'] ) ) {
258
  $orderby = "CAST($wpdb->streammeta.meta_value AS SIGNED)";
314
  $this->found_records = absint( $wpdb->get_var( 'SELECT FOUND_ROWS()' ) );
315
 
316
  // Add meta to the records, when applicable
317
+ if ( empty( $fields ) || in_array( 'meta', $fields, true ) ) {
318
  $results = $this->add_record_meta( $results );
319
  }
320
 
classes/class-settings.php CHANGED
@@ -614,7 +614,7 @@ class Settings {
614
  esc_attr( $section ),
615
  esc_attr( $name ),
616
  esc_attr( $value ),
617
- checked( in_array( $value, $current_value ), true, false )
618
  ),
619
  esc_html( $label )
620
  );
614
  esc_attr( $section ),
615
  esc_attr( $name ),
616
  esc_attr( $value ),
617
+ checked( in_array( $value, $current_value, true ), true, false )
618
  ),
619
  esc_html( $label )
620
  );
connectors/class-connector-bbpress.php CHANGED
@@ -181,7 +181,7 @@ class Connector_bbPress extends Connector {
181
  $data['connector'] = $this->name;
182
  $data['context'] = 'settings';
183
  $data['action'] = 'updated';
184
- } elseif ( 'posts' === $data['connector'] && in_array( $data['context'], array( 'forum', 'topic', 'reply' ) ) ) {
185
  if ( 'reply' === $data['context'] ) {
186
  if ( 'updated' === $data['action'] ) {
187
  $data['message'] = esc_html__( 'Replied on "%1$s"', 'stream' );
@@ -194,7 +194,7 @@ class Connector_bbPress extends Connector {
194
  }
195
 
196
  $data['connector'] = $this->name;
197
- } elseif ( 'taxonomies' === $data['connector'] && in_array( $data['context'], array( 'topic-tag' ) ) ) {
198
  $data['connector'] = $this->name;
199
  }
200
 
181
  $data['connector'] = $this->name;
182
  $data['context'] = 'settings';
183
  $data['action'] = 'updated';
184
+ } elseif ( 'posts' === $data['connector'] && in_array( $data['context'], array( 'forum', 'topic', 'reply' ), true ) ) {
185
  if ( 'reply' === $data['context'] ) {
186
  if ( 'updated' === $data['action'] ) {
187
  $data['message'] = esc_html__( 'Replied on "%1$s"', 'stream' );
194
  }
195
 
196
  $data['connector'] = $this->name;
197
+ } elseif ( 'taxonomies' === $data['connector'] && in_array( $data['context'], array( 'topic-tag' ), true ) ) {
198
  $data['connector'] = $this->name;
199
  }
200
 
connectors/class-connector-buddypress.php CHANGED
@@ -157,7 +157,7 @@ class Connector_BuddyPress extends Connector {
157
  * @return array Action links
158
  */
159
  public function action_links( $links, $record ) {
160
- if ( in_array( $record->context, array( 'components' ) ) ) {
161
  $option_key = $record->get_meta( 'option_key', true );
162
 
163
  if ( 'bp-active-components' === $option_key ) {
@@ -182,14 +182,14 @@ class Connector_BuddyPress extends Connector {
182
  $links[ esc_html__( 'View', 'stream' ) ] = get_permalink( $page_id );
183
  }
184
  }
185
- } elseif ( in_array( $record->context, array( 'settings' ) ) ) {
186
  $links[ esc_html__( 'Edit setting', 'stream' ) ] = add_query_arg(
187
  array(
188
  'page' => $record->get_meta( 'page', true ),
189
  ),
190
  admin_url( 'admin.php' )
191
  );
192
- } elseif ( in_array( $record->context, array( 'groups' ) ) ) {
193
  $group_id = $record->get_meta( 'id', true );
194
  $group = \groups_get_group( array( 'group_id' => $group_id ) );
195
 
@@ -204,7 +204,7 @@ class Connector_BuddyPress extends Connector {
204
  $links[ esc_html__( 'View group', 'stream' ) ] = $visit_url;
205
  $links[ esc_html__( 'Delete group', 'stream' ) ] = $delete_url;
206
  }
207
- } elseif ( in_array( $record->context, array( 'activity' ) ) ) {
208
  $activity_id = $record->get_meta( 'id', true );
209
  $activities = \bp_activity_get( array( 'in' => $activity_id, 'spam' => 'all' ) );
210
  if ( ! empty( $activities['activities'] ) ) {
@@ -225,7 +225,7 @@ class Connector_BuddyPress extends Connector {
225
  }
226
  $links[ esc_html__( 'Delete', 'stream' ) ] = $delete_url;
227
  }
228
- } elseif ( in_array( $record->context, array( 'profile_fields' ) ) ) {
229
  $field_id = $record->get_meta( 'field_id', true );
230
  $group_id = $record->get_meta( 'group_id', true );
231
 
157
  * @return array Action links
158
  */
159
  public function action_links( $links, $record ) {
160
+ if ( in_array( $record->context, array( 'components' ), true ) ) {
161
  $option_key = $record->get_meta( 'option_key', true );
162
 
163
  if ( 'bp-active-components' === $option_key ) {
182
  $links[ esc_html__( 'View', 'stream' ) ] = get_permalink( $page_id );
183
  }
184
  }
185
+ } elseif ( in_array( $record->context, array( 'settings' ), true ) ) {
186
  $links[ esc_html__( 'Edit setting', 'stream' ) ] = add_query_arg(
187
  array(
188
  'page' => $record->get_meta( 'page', true ),
189
  ),
190
  admin_url( 'admin.php' )
191
  );
192
+ } elseif ( in_array( $record->context, array( 'groups' ), true ) ) {
193
  $group_id = $record->get_meta( 'id', true );
194
  $group = \groups_get_group( array( 'group_id' => $group_id ) );
195
 
204
  $links[ esc_html__( 'View group', 'stream' ) ] = $visit_url;
205
  $links[ esc_html__( 'Delete group', 'stream' ) ] = $delete_url;
206
  }
207
+ } elseif ( in_array( $record->context, array( 'activity' ), true ) ) {
208
  $activity_id = $record->get_meta( 'id', true );
209
  $activities = \bp_activity_get( array( 'in' => $activity_id, 'spam' => 'all' ) );
210
  if ( ! empty( $activities['activities'] ) ) {
225
  }
226
  $links[ esc_html__( 'Delete', 'stream' ) ] = $delete_url;
227
  }
228
+ } elseif ( in_array( $record->context, array( 'profile_fields' ), true ) ) {
229
  $field_id = $record->get_meta( 'field_id', true );
230
  $group_id = $record->get_meta( 'group_id', true );
231
 
connectors/class-connector-comments.php CHANGED
@@ -243,7 +243,7 @@ class Connector_Comments extends Connector {
243
  * @param object $comment
244
  */
245
  public function callback_wp_insert_comment( $comment_id, $comment ) {
246
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
247
  return;
248
  }
249
 
@@ -311,7 +311,7 @@ class Connector_Comments extends Connector {
311
  public function callback_edit_comment( $comment_id ) {
312
  $comment = get_comment( $comment_id );
313
 
314
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
315
  return;
316
  }
317
 
@@ -375,7 +375,7 @@ class Connector_Comments extends Connector {
375
  public function callback_delete_comment( $comment_id ) {
376
  $comment = get_comment( $comment_id );
377
 
378
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
379
  return;
380
  }
381
 
@@ -413,7 +413,7 @@ class Connector_Comments extends Connector {
413
  public function callback_trash_comment( $comment_id ) {
414
  $comment = get_comment( $comment_id );
415
 
416
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
417
  return;
418
  }
419
 
@@ -447,7 +447,7 @@ class Connector_Comments extends Connector {
447
  public function callback_untrash_comment( $comment_id ) {
448
  $comment = get_comment( $comment_id );
449
 
450
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
451
  return;
452
  }
453
 
@@ -481,7 +481,7 @@ class Connector_Comments extends Connector {
481
  public function callback_spam_comment( $comment_id ) {
482
  $comment = get_comment( $comment_id );
483
 
484
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
485
  return;
486
  }
487
 
@@ -515,7 +515,7 @@ class Connector_Comments extends Connector {
515
  public function callback_unspam_comment( $comment_id ) {
516
  $comment = get_comment( $comment_id );
517
 
518
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
519
  return;
520
  }
521
 
@@ -549,7 +549,7 @@ class Connector_Comments extends Connector {
549
  * @param object $comment
550
  */
551
  public function callback_transition_comment_status( $new_status, $old_status, $comment ) {
552
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
553
  return;
554
  }
555
 
@@ -591,7 +591,7 @@ class Connector_Comments extends Connector {
591
  $comment_id = $wpdb->last_result[0]->comment_ID;
592
  $comment = get_comment( $comment_id );
593
 
594
- if ( in_array( $comment->comment_type, $this->get_ignored_comment_types() ) ) {
595
  return;
596
  }
597
 
243
  * @param object $comment
244
  */
245
  public function callback_wp_insert_comment( $comment_id, $comment ) {
246
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
247
  return;
248
  }
249
 
311
  public function callback_edit_comment( $comment_id ) {
312
  $comment = get_comment( $comment_id );
313
 
314
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
315
  return;
316
  }
317
 
375
  public function callback_delete_comment( $comment_id ) {
376
  $comment = get_comment( $comment_id );
377
 
378
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
379
  return;
380
  }
381
 
413
  public function callback_trash_comment( $comment_id ) {
414
  $comment = get_comment( $comment_id );
415
 
416
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
417
  return;
418
  }
419
 
447
  public function callback_untrash_comment( $comment_id ) {
448
  $comment = get_comment( $comment_id );
449
 
450
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
451
  return;
452
  }
453
 
481
  public function callback_spam_comment( $comment_id ) {
482
  $comment = get_comment( $comment_id );
483
 
484
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
485
  return;
486
  }
487
 
515
  public function callback_unspam_comment( $comment_id ) {
516
  $comment = get_comment( $comment_id );
517
 
518
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
519
  return;
520
  }
521
 
549
  * @param object $comment
550
  */
551
  public function callback_transition_comment_status( $new_status, $old_status, $comment ) {
552
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
553
  return;
554
  }
555
 
591
  $comment_id = $wpdb->last_result[0]->comment_ID;
592
  $comment = get_comment( $comment_id );
593
 
594
+ if ( in_array( $comment->comment_type, $this->get_ignored_comment_types(), true ) ) {
595
  return;
596
  }
597
 
connectors/class-connector-edd.php CHANGED
@@ -147,10 +147,10 @@ class Connector_EDD extends Connector {
147
  * @return array Action links
148
  */
149
  public function action_links( $links, $record ) {
150
- if ( in_array( $record->context, array( 'downloads' ) ) ) {
151
  $posts_connector = new Connector_Posts();
152
  $links = $posts_connector->action_links( $links, $record );
153
- } elseif ( in_array( $record->context, array( 'discounts' ) ) ) {
154
  $post_type_label = get_post_type_labels( get_post_type_object( 'edd_discount' ) )->singular_name;
155
  $base = admin_url( 'edit.php?post_type=download&page=edd-discounts' );
156
 
@@ -179,7 +179,7 @@ class Connector_EDD extends Connector {
179
  $base
180
  );
181
  }
182
- } elseif ( in_array( $record->context, array( 'download_category', 'download_tag' ) ) ) {
183
  $tax_label = get_taxonomy_labels( get_taxonomy( $record->context ) )->singular_name;
184
  $links[ sprintf( esc_html__( 'Edit %s', 'stream' ), $tax_label ) ] = get_edit_term_link( $record->object_id, $record->get_meta( 'taxonomy', true ) );
185
  } elseif ( 'api_keys' === $record->context ) {
@@ -451,7 +451,7 @@ class Connector_EDD extends Connector {
451
  }
452
 
453
  public function meta( $object_id, $key, $value, $is_add = false ) {
454
- if ( ! in_array( $key, $this->user_meta ) ) {
455
  return false;
456
  }
457
 
147
  * @return array Action links
148
  */
149
  public function action_links( $links, $record ) {
150
+ if ( in_array( $record->context, array( 'downloads' ), true ) ) {
151
  $posts_connector = new Connector_Posts();
152
  $links = $posts_connector->action_links( $links, $record );
153
+ } elseif ( in_array( $record->context, array( 'discounts' ), true ) ) {
154
  $post_type_label = get_post_type_labels( get_post_type_object( 'edd_discount' ) )->singular_name;
155
  $base = admin_url( 'edit.php?post_type=download&page=edd-discounts' );
156
 
179
  $base
180
  );
181
  }
182
+ } elseif ( in_array( $record->context, array( 'download_category', 'download_tag' ), true ) ) {
183
  $tax_label = get_taxonomy_labels( get_taxonomy( $record->context ) )->singular_name;
184
  $links[ sprintf( esc_html__( 'Edit %s', 'stream' ), $tax_label ) ] = get_edit_term_link( $record->object_id, $record->get_meta( 'taxonomy', true ) );
185
  } elseif ( 'api_keys' === $record->context ) {
451
  }
452
 
453
  public function meta( $object_id, $key, $value, $is_add = false ) {
454
+ if ( ! in_array( $key, $this->user_meta, true ) ) {
455
  return false;
456
  }
457
 
connectors/class-connector-installer.php CHANGED
@@ -131,7 +131,7 @@ class Connector_Installer extends Connector {
131
  $type = $extra['type'];
132
  $action = $extra['action'];
133
 
134
- if ( ! in_array( $type, array( 'plugin', 'theme' ) ) ) {
135
  return false;
136
  }
137
 
131
  $type = $extra['type'];
132
  $action = $extra['action'];
133
 
134
+ if ( ! in_array( $type, array( 'plugin', 'theme' ), true ) ) {
135
  return false;
136
  }
137
 
connectors/class-connector-jetpack.php CHANGED
@@ -328,7 +328,7 @@ class Connector_Jetpack extends Connector {
328
  $action = null;
329
  $meta = array();
330
 
331
- if ( in_array( $method, array( 'activate', 'deactivate' ) ) && ! is_null( $data ) ) {
332
  $module_slug = $data;
333
  $module = \Jetpack::get_module( $module_slug );
334
  $module_name = $module['name'];
@@ -340,7 +340,7 @@ class Connector_Jetpack extends Connector {
340
  $module_name,
341
  ( 'activated' === $action ) ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' )
342
  );
343
- } elseif ( in_array( $method, array( 'authorize', 'unlink' ) ) && ! is_null( $data ) ) {
344
  $user_id = intval( $data );
345
 
346
  if ( empty( $user_id ) ) {
@@ -359,7 +359,7 @@ class Connector_Jetpack extends Connector {
359
  ( 'unlink' === $action ) ? esc_html__( 'unlinked', 'stream' ) : esc_html__( 'linked', 'stream' ),
360
  ( 'unlink' === $action ) ? esc_html__( 'from', 'stream' ) : esc_html__( 'to', 'stream' )
361
  );
362
- } elseif ( in_array( $method, array( 'register', 'disconnect', 'subsiteregister', 'subsitedisconnect' ) ) ) {
363
  $context = 'blogs';
364
  $action = str_replace( 'subsite', '', $method );
365
  $is_multisite = ( 0 === strpos( $method, 'subsite' ) );
328
  $action = null;
329
  $meta = array();
330
 
331
+ if ( in_array( $method, array( 'activate', 'deactivate' ), true ) && ! is_null( $data ) ) {
332
  $module_slug = $data;
333
  $module = \Jetpack::get_module( $module_slug );
334
  $module_name = $module['name'];
340
  $module_name,
341
  ( 'activated' === $action ) ? esc_html__( 'activated', 'stream' ) : esc_html__( 'deactivated', 'stream' )
342
  );
343
+ } elseif ( in_array( $method, array( 'authorize', 'unlink' ), true ) && ! is_null( $data ) ) {
344
  $user_id = intval( $data );
345
 
346
  if ( empty( $user_id ) ) {
359
  ( 'unlink' === $action ) ? esc_html__( 'unlinked', 'stream' ) : esc_html__( 'linked', 'stream' ),
360
  ( 'unlink' === $action ) ? esc_html__( 'from', 'stream' ) : esc_html__( 'to', 'stream' )
361
  );
362
+ } elseif ( in_array( $method, array( 'register', 'disconnect', 'subsiteregister', 'subsitedisconnect' ), true ) ) {
363
  $context = 'blogs';
364
  $action = str_replace( 'subsite', '', $method );
365
  $is_multisite = ( 0 === strpos( $method, 'subsite' ) );
connectors/class-connector-menus.php CHANGED
@@ -82,7 +82,7 @@ class Connector_Menus extends Connector {
82
  $menus = wp_get_nav_menus();
83
  $menu_ids = wp_list_pluck( $menus, 'term_id' );
84
 
85
- if ( in_array( $record->object_id, $menu_ids ) ) {
86
  $links[ esc_html__( 'Edit Menu', 'stream' ) ] = admin_url( 'nav-menus.php?action=edit&menu=' . $record->object_id );
87
  }
88
  }
82
  $menus = wp_get_nav_menus();
83
  $menu_ids = wp_list_pluck( $menus, 'term_id' );
84
 
85
+ if ( in_array( $record->object_id, $menu_ids, true ) ) {
86
  $links[ esc_html__( 'Edit Menu', 'stream' ) ] = admin_url( 'nav-menus.php?action=edit&menu=' . $record->object_id );
87
  }
88
  }
connectors/class-connector-posts.php CHANGED
@@ -146,11 +146,11 @@ class Connector_Posts extends Connector {
146
  * @param \WP_Post $post
147
  */
148
  public function callback_transition_post_status( $new, $old, $post ) {
149
- if ( in_array( $post->post_type, $this->get_excluded_post_types() ) ) {
150
  return;
151
  }
152
 
153
- if ( in_array( $new, array( 'auto-draft', 'inherit' ) ) ) {
154
  return;
155
  } elseif ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
156
  return;
@@ -282,7 +282,7 @@ class Connector_Posts extends Connector {
282
  $post = get_post( $post_id );
283
 
284
  // We check if post is an instance of WP_Post as it doesn't always resolve in unit testing
285
- if ( ! ( $post instanceof \WP_Post ) || in_array( $post->post_type, $this->get_excluded_post_types() ) ) {
286
  return;
287
  }
288
 
@@ -361,9 +361,8 @@ class Connector_Posts extends Connector {
361
  $order = ( $previous ) ? 'DESC' : 'ASC';
362
 
363
  global $wpdb;
364
-
365
  $revision_id = $wpdb->get_var( // db call okay
366
- // @codingStandardsIgnoreStart
367
  $wpdb->prepare(
368
  "SELECT p.ID
369
  FROM $wpdb->posts AS p
@@ -375,8 +374,9 @@ class Connector_Posts extends Connector {
375
  $revision->post_date,
376
  $revision->post_parent
377
  )
378
- // @codingStandardsIgnoreEnd prepare okay
379
  );
 
 
380
 
381
  $revision_id = absint( $revision_id );
382
 
146
  * @param \WP_Post $post
147
  */
148
  public function callback_transition_post_status( $new, $old, $post ) {
149
+ if ( in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
150
  return;
151
  }
152
 
153
+ if ( in_array( $new, array( 'auto-draft', 'inherit' ), true ) ) {
154
  return;
155
  } elseif ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
156
  return;
282
  $post = get_post( $post_id );
283
 
284
  // We check if post is an instance of WP_Post as it doesn't always resolve in unit testing
285
+ if ( ! ( $post instanceof \WP_Post ) || in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
286
  return;
287
  }
288
 
361
  $order = ( $previous ) ? 'DESC' : 'ASC';
362
 
363
  global $wpdb;
364
+ // @codingStandardsIgnoreStart
365
  $revision_id = $wpdb->get_var( // db call okay
 
366
  $wpdb->prepare(
367
  "SELECT p.ID
368
  FROM $wpdb->posts AS p
374
  $revision->post_date,
375
  $revision->post_parent
376
  )
 
377
  );
378
+ // @codingStandardsIgnoreEnd
379
+ // prepare okay
380
 
381
  $revision_id = absint( $revision_id );
382
 
connectors/class-connector-settings.php CHANGED
@@ -287,7 +287,7 @@ class Connector_Settings extends Connector {
287
 
288
  if ( isset( $contexts[ $option_name ] ) ) {
289
  foreach ( $contexts[ $option_name ] as $context => $keys ) {
290
- if ( in_array( $key, $keys ) ) {
291
  return $context;
292
  }
293
  }
@@ -313,7 +313,7 @@ class Connector_Settings extends Connector {
313
  );
314
 
315
  if ( isset( $ignored[ $option_name ] ) ) {
316
- return in_array( $key, $ignored[ $option_name ] );
317
  }
318
 
319
  return false;
@@ -446,7 +446,7 @@ class Connector_Settings extends Connector {
446
  );
447
  },
448
  'applicable' => function( $submenu, $record ) {
449
- return $record->context === 'wp_stream';
450
  },
451
  ),
452
  'background_header' => array(
@@ -458,7 +458,7 @@ class Connector_Settings extends Connector {
458
  return add_query_arg( 'page', $rule['submenu_slug']( $record ), admin_url( $rule['menu_slug'] ) );
459
  },
460
  'applicable' => function( $submenu, $record ) {
461
- return in_array( $record->context, array( 'custom_header', 'custom_background' ) );
462
  },
463
  ),
464
  'general' => array(
@@ -490,7 +490,7 @@ class Connector_Settings extends Connector {
490
  ),
491
  );
492
 
493
- if ( 'settings' !== $record->context && in_array( $record->context, array_keys( $context_labels ) ) ) {
494
  global $submenu;
495
 
496
  $applicable_rules = array_filter(
@@ -541,7 +541,7 @@ class Connector_Settings extends Connector {
541
  }
542
 
543
  /**
544
- * Trigger this connector from WP CLI, only for known Settings
545
  *
546
  * @action update_option
547
  *
@@ -550,7 +550,7 @@ class Connector_Settings extends Connector {
550
  * @param mixed $value
551
  */
552
  public function callback_update_option( $option, $value, $old_value ) {
553
- if ( defined( '\WP_CLI' ) && \WP_CLI && array_key_exists( $option, $this->labels ) ) {
554
  $this->callback_updated_option( $option, $value, $old_value );
555
  }
556
  }
@@ -644,7 +644,7 @@ class Connector_Settings extends Connector {
644
  );
645
 
646
  foreach ( $options as $key => $opts ) {
647
- if ( in_array( $option, $opts ) ) {
648
  $context = $key;
649
  break;
650
  }
287
 
288
  if ( isset( $contexts[ $option_name ] ) ) {
289
  foreach ( $contexts[ $option_name ] as $context => $keys ) {
290
+ if ( in_array( $key, $keys, true ) ) {
291
  return $context;
292
  }
293
  }
313
  );
314
 
315
  if ( isset( $ignored[ $option_name ] ) ) {
316
+ return in_array( $key, $ignored[ $option_name ], true );
317
  }
318
 
319
  return false;
446
  );
447
  },
448
  'applicable' => function( $submenu, $record ) {
449
+ return 'wp_stream' === $record->context;
450
  },
451
  ),
452
  'background_header' => array(
458
  return add_query_arg( 'page', $rule['submenu_slug']( $record ), admin_url( $rule['menu_slug'] ) );
459
  },
460
  'applicable' => function( $submenu, $record ) {
461
+ return in_array( $record->context, array( 'custom_header', 'custom_background' ), true );
462
  },
463
  ),
464
  'general' => array(
490
  ),
491
  );
492
 
493
+ if ( 'settings' !== $record->context && in_array( $record->context, array_keys( $context_labels ), true ) ) {
494
  global $submenu;
495
 
496
  $applicable_rules = array_filter(
541
  }
542
 
543
  /**
544
+ * Trigger this connector from WP CLI or the Customizer, only for known Settings
545
  *
546
  * @action update_option
547
  *
550
  * @param mixed $value
551
  */
552
  public function callback_update_option( $option, $value, $old_value ) {
553
+ if ( ( defined( '\WP_CLI' ) && \WP_CLI || did_action( 'customize_save' ) ) && array_key_exists( $option, $this->labels ) ) {
554
  $this->callback_updated_option( $option, $value, $old_value );
555
  }
556
  }
644
  );
645
 
646
  foreach ( $options as $key => $opts ) {
647
+ if ( in_array( $option, $opts, true ) ) {
648
  $context = $key;
649
  break;
650
  }
connectors/class-connector-taxonomies.php CHANGED
@@ -140,7 +140,7 @@ class Connector_Taxonomies extends Connector {
140
  * @param string $taxonomy
141
  */
142
  public function callback_created_term( $term_id, $tt_id, $taxonomy ) {
143
- if ( in_array( $taxonomy, $this->get_excluded_taxonomies() ) ) {
144
  return;
145
  }
146
 
@@ -173,7 +173,7 @@ class Connector_Taxonomies extends Connector {
173
  * @param object $deleted_term
174
  */
175
  public function callback_delete_term( $term_id, $tt_id, $taxonomy, $deleted_term ) {
176
- if ( in_array( $taxonomy, $this->get_excluded_taxonomies() ) ) {
177
  return;
178
  }
179
 
@@ -209,7 +209,7 @@ class Connector_Taxonomies extends Connector {
209
  }
210
 
211
  public function callback_edited_term( $term_id, $tt_id, $taxonomy ) {
212
- if ( in_array( $taxonomy, $this->get_excluded_taxonomies() ) ) {
213
  return;
214
  }
215
 
140
  * @param string $taxonomy
141
  */
142
  public function callback_created_term( $term_id, $tt_id, $taxonomy ) {
143
+ if ( in_array( $taxonomy, $this->get_excluded_taxonomies(), true ) ) {
144
  return;
145
  }
146
 
173
  * @param object $deleted_term
174
  */
175
  public function callback_delete_term( $term_id, $tt_id, $taxonomy, $deleted_term ) {
176
+ if ( in_array( $taxonomy, $this->get_excluded_taxonomies(), true ) ) {
177
  return;
178
  }
179
 
209
  }
210
 
211
  public function callback_edited_term( $term_id, $tt_id, $taxonomy ) {
212
+ if ( in_array( $taxonomy, $this->get_excluded_taxonomies(), true ) ) {
213
  return;
214
  }
215
 
connectors/class-connector-users.php CHANGED
@@ -116,7 +116,7 @@ class Connector_Users extends Connector {
116
  $labels = array();
117
 
118
  foreach ( $roles as $role => $label ) {
119
- if ( in_array( $role, (array) $user->roles ) ) {
120
  $labels[] = translate_user_role( $label );
121
  }
122
  }
116
  $labels = array();
117
 
118
  foreach ( $roles as $role => $label ) {
119
+ if ( in_array( $role, (array) $user->roles, true ) ) {
120
  $labels[] = translate_user_role( $label );
121
  }
122
  }
connectors/class-connector-widgets.php CHANGED
@@ -183,7 +183,7 @@ class Connector_Widgets extends Connector {
183
  $sidebar_id = '';
184
 
185
  foreach ( $old as $old_sidebar_id => $old_widget_ids ) {
186
- if ( in_array( $widget_id, $old_widget_ids ) ) {
187
  $sidebar_id = $old_sidebar_id;
188
  break;
189
  }
@@ -234,7 +234,7 @@ class Connector_Widgets extends Connector {
234
  $sidebar_id = '';
235
 
236
  foreach ( $new as $new_sidebar_id => $new_widget_ids ) {
237
- if ( in_array( $widget_id, $new_widget_ids ) ) {
238
  $sidebar_id = $new_sidebar_id;
239
  break;
240
  }
@@ -288,7 +288,7 @@ class Connector_Widgets extends Connector {
288
  $sidebar_id = '';
289
 
290
  foreach ( $old as $old_sidebar_id => $old_widget_ids ) {
291
- if ( in_array( $widget_id, $old_widget_ids ) ) {
292
  $sidebar_id = $old_sidebar_id;
293
  break;
294
  }
@@ -341,7 +341,7 @@ class Connector_Widgets extends Connector {
341
  $sidebar_id = '';
342
 
343
  foreach ( $new as $new_sidebar_id => $new_widget_ids ) {
344
- if ( in_array( $widget_id, $new_widget_ids ) ) {
345
  $sidebar_id = $new_sidebar_id;
346
  break;
347
  }
@@ -441,7 +441,7 @@ class Connector_Widgets extends Connector {
441
  // Now find the sidebar that the widget was originally located in, as long it is not wp_inactive_widgets
442
  $old_sidebar_id = null;
443
  foreach ( $old as $sidebar_id => $old_widget_ids ) {
444
- if ( in_array( $widget_id, $old_widget_ids ) ) {
445
  $old_sidebar_id = $sidebar_id;
446
  break;
447
  }
@@ -804,7 +804,7 @@ class Connector_Widgets extends Connector {
804
  unset( $sidebars_widgets['array_version'] );
805
 
806
  foreach ( $sidebars_widgets as $sidebar_id => $widget_ids ) {
807
- if ( in_array( $widget_id, $widget_ids ) ) {
808
  return $sidebar_id;
809
  }
810
  }
183
  $sidebar_id = '';
184
 
185
  foreach ( $old as $old_sidebar_id => $old_widget_ids ) {
186
+ if ( in_array( $widget_id, $old_widget_ids, true ) ) {
187
  $sidebar_id = $old_sidebar_id;
188
  break;
189
  }
234
  $sidebar_id = '';
235
 
236
  foreach ( $new as $new_sidebar_id => $new_widget_ids ) {
237
+ if ( in_array( $widget_id, $new_widget_ids, true ) ) {
238
  $sidebar_id = $new_sidebar_id;
239
  break;
240
  }
288
  $sidebar_id = '';
289
 
290
  foreach ( $old as $old_sidebar_id => $old_widget_ids ) {
291
+ if ( in_array( $widget_id, $old_widget_ids, true ) ) {
292
  $sidebar_id = $old_sidebar_id;
293
  break;
294
  }
341
  $sidebar_id = '';
342
 
343
  foreach ( $new as $new_sidebar_id => $new_widget_ids ) {
344
+ if ( in_array( $widget_id, $new_widget_ids, true ) ) {
345
  $sidebar_id = $new_sidebar_id;
346
  break;
347
  }
441
  // Now find the sidebar that the widget was originally located in, as long it is not wp_inactive_widgets
442
  $old_sidebar_id = null;
443
  foreach ( $old as $sidebar_id => $old_widget_ids ) {
444
+ if ( in_array( $widget_id, $old_widget_ids, true ) ) {
445
  $old_sidebar_id = $sidebar_id;
446
  break;
447
  }
804
  unset( $sidebars_widgets['array_version'] );
805
 
806
  foreach ( $sidebars_widgets as $sidebar_id => $widget_ids ) {
807
+ if ( in_array( $widget_id, $widget_ids, true ) ) {
808
  return $sidebar_id;
809
  }
810
  }
connectors/class-connector-woocommerce.php CHANGED
@@ -209,7 +209,7 @@ class Connector_Woocommerce extends Connector {
209
  * @return array Action links
210
  */
211
  public function action_links( $links, $record ) {
212
- if ( in_array( $record->context, $this->post_types ) && get_post( $record->object_id ) ) {
213
  if ( $link = get_edit_post_link( $record->object_id ) ) {
214
  $posts_connector = new Connector_Posts();
215
  $post_type_name = $posts_connector->get_post_type_name( get_post_type( $record->object_id ) );
@@ -293,7 +293,7 @@ class Connector_Woocommerce extends Connector {
293
  }
294
 
295
  // Don't track minor status change actions
296
- if ( in_array( wp_stream_filter_input( INPUT_GET, 'action' ), array( 'mark_processing', 'mark_on-hold', 'mark_completed' ) ) || defined( 'DOING_AJAX' ) ) {
297
  return;
298
  }
299
 
@@ -302,7 +302,7 @@ class Connector_Woocommerce extends Connector {
302
  return;
303
  }
304
 
305
- if ( in_array( $new, array( 'auto-draft', 'draft', 'inherit' ) ) ) {
306
  return;
307
  } elseif ( 'auto-draft' === $old && 'publish' === $new ) {
308
  $message = esc_html_x(
@@ -612,9 +612,9 @@ class Connector_Woocommerce extends Connector {
612
  }
613
 
614
  // Change connector::posts records
615
- if ( 'posts' === $record['connector'] && in_array( $record['context'], $this->post_types ) ) {
616
  $recordarr[ $key ]['connector'] = $this->name;
617
- } elseif ( 'taxonomies' === $record['connector'] && in_array( $record['context'], $this->taxonomies ) ) {
618
  $recordarr[ $key ]['connector'] = $this->name;
619
  } elseif ( 'settings' === $record['connector'] ) {
620
  $option = isset( $record['meta']['option_key'] ) ? $record['meta']['option_key'] : false;
@@ -703,7 +703,7 @@ class Connector_Woocommerce extends Connector {
703
  $_fields = array_filter(
704
  $page->get_settings( $section_key ),
705
  function( $item ) {
706
- return isset( $item['id'] ) && ( ! in_array( $item['type'], array( 'title', 'sectionend' ) ) );
707
  }
708
  );
709
 
209
  * @return array Action links
210
  */
211
  public function action_links( $links, $record ) {
212
+ if ( in_array( $record->context, $this->post_types, true ) && get_post( $record->object_id ) ) {
213
  if ( $link = get_edit_post_link( $record->object_id ) ) {
214
  $posts_connector = new Connector_Posts();
215
  $post_type_name = $posts_connector->get_post_type_name( get_post_type( $record->object_id ) );
293
  }
294
 
295
  // Don't track minor status change actions
296
+ if ( in_array( wp_stream_filter_input( INPUT_GET, 'action' ), array( 'mark_processing', 'mark_on-hold', 'mark_completed' ), true ) || defined( 'DOING_AJAX' ) ) {
297
  return;
298
  }
299
 
302
  return;
303
  }
304
 
305
+ if ( in_array( $new, array( 'auto-draft', 'draft', 'inherit' ), true ) ) {
306
  return;
307
  } elseif ( 'auto-draft' === $old && 'publish' === $new ) {
308
  $message = esc_html_x(
612
  }
613
 
614
  // Change connector::posts records
615
+ if ( 'posts' === $record['connector'] && in_array( $record['context'], $this->post_types, true ) ) {
616
  $recordarr[ $key ]['connector'] = $this->name;
617
+ } elseif ( 'taxonomies' === $record['connector'] && in_array( $record['context'], $this->taxonomies, true ) ) {
618
  $recordarr[ $key ]['connector'] = $this->name;
619
  } elseif ( 'settings' === $record['connector'] ) {
620
  $option = isset( $record['meta']['option_key'] ) ? $record['meta']['option_key'] : false;
703
  $_fields = array_filter(
704
  $page->get_settings( $section_key ),
705
  function( $item ) {
706
+ return isset( $item['id'] ) && ( ! in_array( $item['type'], array( 'title', 'sectionend' ), true ) );
707
  }
708
  );
709
 
exporters/class-exporter-csv.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Exporter_CSV extends Exporter{
5
+ /**
6
+ * Exporter name
7
+ *
8
+ * @var string
9
+ */
10
+ public $name = 'CSV';
11
+
12
+ /**
13
+ * Exporter slug
14
+ *
15
+ * @var string
16
+ */
17
+ public $slug = 'csv';
18
+
19
+ /**
20
+ * Outputs CSV data for download
21
+ *
22
+ * @param array $data Array of data to output.
23
+ * @param array $columns Column names included in data set.
24
+ * @return void
25
+ */
26
+ public function output_file( $data, $columns ) {
27
+ if ( ! defined( 'WP_STREAM_TESTS' ) || ( defined( 'WP_STREAM_TESTS' ) && ! WP_STREAM_TESTS ) ) {
28
+ header( 'Content-type: text/csv' );
29
+ header( 'Content-Disposition: attachment; filename="stream.csv"' );
30
+ }
31
+
32
+ $output = join( ',', array_values( $columns ) ) . "\n";
33
+ foreach ( $data as $row ) {
34
+ $output .= join( ',', $row ) . "\n";
35
+ }
36
+
37
+ echo $output; // @codingStandardsIgnoreLine text-only output
38
+ if ( ! defined( 'WP_STREAM_TESTS' ) || ( defined( 'WP_STREAM_TESTS' ) && ! WP_STREAM_TESTS ) ) {
39
+ exit;
40
+ }
41
+ }
42
+ }
exporters/class-exporter-json.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Exporter_JSON extends Exporter {
5
+ /**
6
+ * Exporter name
7
+ *
8
+ * @var string
9
+ */
10
+ public $name = 'JSON';
11
+
12
+ /**
13
+ * Exporter slug
14
+ *
15
+ * @var string
16
+ */
17
+ public $slug = 'json';
18
+
19
+ /**
20
+ * Outputs JSON data for download
21
+ *
22
+ * @param array $data Array of data to output.
23
+ * @param array $columns Column names included in data set.
24
+ * @return void
25
+ */
26
+ public function output_file( $data, $columns ) {
27
+ if ( ! defined( 'WP_STREAM_TESTS' ) || ( defined( 'WP_STREAM_TESTS' ) && ! WP_STREAM_TESTS ) ) {
28
+ header( 'Content-type: text/json' );
29
+ header( 'Content-Disposition: attachment; filename="stream.json"' );
30
+ }
31
+
32
+ if ( function_exists( 'wp_json_encode' ) ) {
33
+ $output = wp_json_encode( $data );
34
+ } else {
35
+ $output = json_encode( $data ); // @codingStandardsIgnoreLine fallback to discouraged function
36
+ }
37
+
38
+ echo $output; // @codingStandardsIgnoreLine text-only output
39
+
40
+ if ( ! defined( 'WP_STREAM_TESTS' ) || ( defined( 'WP_STREAM_TESTS' ) && ! WP_STREAM_TESTS ) ) {
41
+ exit;
42
+ }
43
+ }
44
+ }
phpcs.ruleset.xml DELETED
@@ -1,12 +0,0 @@
1
- <?xml version="1.0"?>
2
-
3
- <ruleset name="WordPress Coding Standards for WP Stream">
4
-
5
- <rule ref="WordPress-Extra" />
6
-
7
- <exclude-pattern>*/tests/*</exclude-pattern>
8
- <exclude-pattern>*/includes/lib/*</exclude-pattern>
9
- <exclude-pattern>*/ui/lib/*</exclude-pattern>
10
- <exclude-pattern>*/dev-lib/*</exclude-pattern>
11
-
12
- </ruleset>
 
 
 
 
 
 
 
 
 
 
 
 
readme.md CHANGED
@@ -6,12 +6,12 @@ Stream is the easiest and safest way to track content changes happening to your
6
 
7
  **Contributors:** [fjarrett](https://profiles.wordpress.org/fjarrett), [lukecarbis](https://profiles.wordpress.org/lukecarbis), [shadyvb](https://profiles.wordpress.org/shadyvb), [westonruter](https://profiles.wordpress.org/westonruter), [stream](https://profiles.wordpress.org/stream), [xwp](https://profiles.wordpress.org/xwp)
8
  **Tags:** [actions](https://wordpress.org/plugins/tags/actions), [activity](https://wordpress.org/plugins/tags/activity), [activity log](https://wordpress.org/plugins/tags/activity log), [activity logs](https://wordpress.org/plugins/tags/activity logs), [admin actions](https://wordpress.org/plugins/tags/admin actions), [analytics](https://wordpress.org/plugins/tags/analytics), [audit](https://wordpress.org/plugins/tags/audit), [audit log](https://wordpress.org/plugins/tags/audit log), [audit logs](https://wordpress.org/plugins/tags/audit logs), [change](https://wordpress.org/plugins/tags/change), [changes](https://wordpress.org/plugins/tags/changes), [dashboard](https://wordpress.org/plugins/tags/dashboard), [log](https://wordpress.org/plugins/tags/log), [logs](https://wordpress.org/plugins/tags/logs), [stream](https://wordpress.org/plugins/tags/stream), [tracking](https://wordpress.org/plugins/tags/tracking), [troubleshooting](https://wordpress.org/plugins/tags/troubleshooting), [wp stream](https://wordpress.org/plugins/tags/wp stream)
9
- **Requires at least:** 3.7
10
- **Tested up to:** 4.4
11
- **Stable tag:** 3.0.4
12
  **License:** [GPLv2 or later](https://www.gnu.org/licenses/gpl-2.0.html)
13
 
14
- [![Build Status](https://travis-ci.org/xwp/stream.png?branch=master)](https://travis-ci.org/xwp/stream) [![Join the chat at https://gitter.im/xwp/stream](https://badges.gitter.im/Joinhat.svg)](https://gitter.im/xwp/stream)
15
 
16
  ## Description ##
17
 
@@ -89,6 +89,17 @@ Thank you for wanting to make Stream better for everyone! We salute you.
89
 
90
  ## Changelog ##
91
 
 
 
 
 
 
 
 
 
 
 
 
92
  ### 3.0.4 - November 27, 2015 ###
93
  * Tweak: Better descriptions when a post changes status ([0eada10](https://github.com/xwp/stream/commit/0eada108b443ed3b6f9bdae3f1e4c87c77128a0a))
94
  * Fix: Stream no longer crashes every time it tries to use a Jetpack ([#798](https://github.com/xwp/stream/pull/798))
@@ -96,6 +107,8 @@ Thank you for wanting to make Stream better for everyone! We salute you.
96
  * Fix: Logging in / out of a Multisite install is now possible ([#801](https://github.com/xwp/stream/pull/801))
97
  * Fix: The Settings connector now works with WP CLI ([78a56b2](https://github.com/xwp/stream/commit/78a56b2c6b33b4f41c7b4f1f256a4d03ad42b2cb))
98
 
 
 
99
  ### 3.0.3 - November 6, 2015 ###
100
  * Tweak: Better compatibility with upcoming WordPress 4.4 ([2b2493c](https://github.com/xwp/stream/commit/2b2493ccb3ef6cba5aeb773433fdb5f0d414e8f3))
101
  * Tweak: Minor security improvements
@@ -106,6 +119,8 @@ Thank you for wanting to make Stream better for everyone! We salute you.
106
  * Fix: Internet Explorer 8 fix!! IE8!? Come on, people, it's 2015. ([#789](https://github.com/xwp/stream/pull/789))
107
  * Fix: EDD connector bug ([#790](https://github.com/xwp/stream/pull/790))
108
 
 
 
109
  ### 3.0.2 - October 2, 2015 ###
110
  * Tweak: Helper function for running Stream queries added ([#774](https://github.com/xwp/stream/pull/774))
111
  * Tweak: Migration dialog removed ([76e809f](https://github.com/xwp/stream/commit/76e809f9abb3dd691b755cf943b50a76a3ffb488))
@@ -117,7 +132,7 @@ Thank you for wanting to make Stream better for everyone! We salute you.
117
  * Fix: Jetpack is now able to connect without error while Stream is active ([#768](https://github.com/xwp/stream/pull/768))
118
  * Fix: Reset Filters text no longer wraps to a second line ([#765](https://github.com/xwp/stream/pull/765))
119
 
120
- Props [@lukecarbis](https://github.com/lukecarbis)
121
 
122
  ### 3.0.1 - September 2, 2015 ###
123
  * New: Stream and [User Switching](https://wordpress.org/plugins/user-switching/) are now besties ([#744](https://github.com/xwp/stream/pull/744))
6
 
7
  **Contributors:** [fjarrett](https://profiles.wordpress.org/fjarrett), [lukecarbis](https://profiles.wordpress.org/lukecarbis), [shadyvb](https://profiles.wordpress.org/shadyvb), [westonruter](https://profiles.wordpress.org/westonruter), [stream](https://profiles.wordpress.org/stream), [xwp](https://profiles.wordpress.org/xwp)
8
  **Tags:** [actions](https://wordpress.org/plugins/tags/actions), [activity](https://wordpress.org/plugins/tags/activity), [activity log](https://wordpress.org/plugins/tags/activity log), [activity logs](https://wordpress.org/plugins/tags/activity logs), [admin actions](https://wordpress.org/plugins/tags/admin actions), [analytics](https://wordpress.org/plugins/tags/analytics), [audit](https://wordpress.org/plugins/tags/audit), [audit log](https://wordpress.org/plugins/tags/audit log), [audit logs](https://wordpress.org/plugins/tags/audit logs), [change](https://wordpress.org/plugins/tags/change), [changes](https://wordpress.org/plugins/tags/changes), [dashboard](https://wordpress.org/plugins/tags/dashboard), [log](https://wordpress.org/plugins/tags/log), [logs](https://wordpress.org/plugins/tags/logs), [stream](https://wordpress.org/plugins/tags/stream), [tracking](https://wordpress.org/plugins/tags/tracking), [troubleshooting](https://wordpress.org/plugins/tags/troubleshooting), [wp stream](https://wordpress.org/plugins/tags/wp stream)
9
+ **Requires at least:** 3.9
10
+ **Tested up to:** 4.5
11
+ **Stable tag:** 3.0.5
12
  **License:** [GPLv2 or later](https://www.gnu.org/licenses/gpl-2.0.html)
13
 
14
+ [![Build Status](https://travis-ci.org/xwp/stream.svg?branch=master)](https://travis-ci.org/xwp/stream) [![Join the chat at https://gitter.im/xwp/stream](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/xwp/stream)
15
 
16
  ## Description ##
17
 
89
 
90
  ## Changelog ##
91
 
92
+ ### 3.0.5 - March 15, 2015 ###
93
+ * New: Export your Stream records as CSV or JSON. ([#823](https://github.com/xwp/stream/pull/823))
94
+ * Tweak: More mobile responsive list table ([#810](https://github.com/xwp/stream/pull/810))
95
+ * Tweak: Better Javascript conflict prevention ([#812](https://github.com/xwp/stream/pull/812))
96
+ * Tweak: Minor styling updates. It's about attention to detail. ([#826](https://github.com/xwp/stream/pull/826))
97
+ * Fix: Gravity Forms error when adding a note ([#811](https://github.com/xwp/stream/pull/811))
98
+ * Fix: In some instances, custom roles weren't being logged by Stream ([#824](https://github.com/xwp/stream/pull/824))
99
+ * Fix: The Customiser fix you've been waiting for! Stream now properly records changes made from the Customiser. ([#827](https://github.com/xwp/stream/pull/827))
100
+
101
+ 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)
102
+
103
  ### 3.0.4 - November 27, 2015 ###
104
  * Tweak: Better descriptions when a post changes status ([0eada10](https://github.com/xwp/stream/commit/0eada108b443ed3b6f9bdae3f1e4c87c77128a0a))
105
  * Fix: Stream no longer crashes every time it tries to use a Jetpack ([#798](https://github.com/xwp/stream/pull/798))
107
  * Fix: Logging in / out of a Multisite install is now possible ([#801](https://github.com/xwp/stream/pull/801))
108
  * Fix: The Settings connector now works with WP CLI ([78a56b2](https://github.com/xwp/stream/commit/78a56b2c6b33b4f41c7b4f1f256a4d03ad42b2cb))
109
 
110
+ Props [@lukecarbis](https://github.com/lukecarbis)
111
+
112
  ### 3.0.3 - November 6, 2015 ###
113
  * Tweak: Better compatibility with upcoming WordPress 4.4 ([2b2493c](https://github.com/xwp/stream/commit/2b2493ccb3ef6cba5aeb773433fdb5f0d414e8f3))
114
  * Tweak: Minor security improvements
119
  * Fix: Internet Explorer 8 fix!! IE8!? Come on, people, it's 2015. ([#789](https://github.com/xwp/stream/pull/789))
120
  * Fix: EDD connector bug ([#790](https://github.com/xwp/stream/pull/790))
121
 
122
+ Props [@lukecarbis](https://github.com/lukecarbis), [@rob](https://github.com/rob), [greguly](https://github.com/greguly)
123
+
124
  ### 3.0.2 - October 2, 2015 ###
125
  * Tweak: Helper function for running Stream queries added ([#774](https://github.com/xwp/stream/pull/774))
126
  * Tweak: Migration dialog removed ([76e809f](https://github.com/xwp/stream/commit/76e809f9abb3dd691b755cf943b50a76a3ffb488))
132
  * Fix: Jetpack is now able to connect without error while Stream is active ([#768](https://github.com/xwp/stream/pull/768))
133
  * Fix: Reset Filters text no longer wraps to a second line ([#765](https://github.com/xwp/stream/pull/765))
134
 
135
+ Props [@lukecarbis](https://github.com/lukecarbis), Props [@sirjonathan](https://github.com/sirjonathan)
136
 
137
  ### 3.0.1 - September 2, 2015 ###
138
  * New: Stream and [User Switching](https://wordpress.org/plugins/user-switching/) are now besties ([#744](https://github.com/xwp/stream/pull/744))
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Stream ===
2
  Contributors: fjarrett, lukecarbis, shadyvb, westonruter, stream, xwp
3
  Tags: actions, activity, activity log, activity logs, admin actions, analytics, audit, audit log, audit logs, change, changes, dashboard, log, logs, stream, tracking, troubleshooting, wp stream
4
- Requires at least: 3.7
5
- Tested up to: 4.4
6
- Stable tag: 3.0.4
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -77,6 +77,18 @@ Thank you for wanting to make Stream better for everyone! We salute you.
77
 
78
  == Changelog ==
79
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  = 3.0.4 - November 27, 2015 =
81
 
82
  * Tweak: Better descriptions when a post changes status ([0eada10](https://github.com/xwp/stream/commit/0eada108b443ed3b6f9bdae3f1e4c87c77128a0a))
@@ -85,6 +97,8 @@ Thank you for wanting to make Stream better for everyone! We salute you.
85
  * Fix: Logging in / out of a Multisite install is now possible ([#801](https://github.com/xwp/stream/pull/801))
86
  * Fix: The Settings connector now works with WP CLI ([78a56b2](https://github.com/xwp/stream/commit/78a56b2c6b33b4f41c7b4f1f256a4d03ad42b2cb))
87
 
 
 
88
  = 3.0.3 - November 6, 2015 =
89
 
90
  * Tweak: Better compatibility with upcoming WordPress 4.4 ([2b2493c](https://github.com/xwp/stream/commit/2b2493ccb3ef6cba5aeb773433fdb5f0d414e8f3))
@@ -96,6 +110,8 @@ Thank you for wanting to make Stream better for everyone! We salute you.
96
  * Fix: Internet Explorer 8 fix!! IE8!? Come on, people, it's 2015. ([#789](https://github.com/xwp/stream/pull/789))
97
  * Fix: EDD connector bug ([#790](https://github.com/xwp/stream/pull/790))
98
 
 
 
99
  = 3.0.2 - October 2, 2015 =
100
 
101
  * Tweak: Helper function for running Stream queries added ([#774](https://github.com/xwp/stream/pull/774))
@@ -108,7 +124,7 @@ Thank you for wanting to make Stream better for everyone! We salute you.
108
  * Fix: Jetpack is now able to connect without error while Stream is active ([#768](https://github.com/xwp/stream/pull/768))
109
  * Fix: Reset Filters text no longer wraps to a second line ([#765](https://github.com/xwp/stream/pull/765))
110
 
111
- Props [@lukecarbis](https://github.com/lukecarbis)
112
 
113
  = 3.0.1 - September 2, 2015 =
114
 
1
  === Stream ===
2
  Contributors: fjarrett, lukecarbis, shadyvb, westonruter, stream, xwp
3
  Tags: actions, activity, activity log, activity logs, admin actions, analytics, audit, audit log, audit logs, 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.5
7
  License: GPLv2 or later
8
  License URI: https://www.gnu.org/licenses/gpl-2.0.html
9
 
77
 
78
  == Changelog ==
79
 
80
+ = 3.0.5 - March 15, 2015 =
81
+
82
+ * New: Export your Stream records as CSV or JSON. ([#823](https://github.com/xwp/stream/pull/823))
83
+ * Tweak: More mobile responsive list table ([#810](https://github.com/xwp/stream/pull/810))
84
+ * Tweak: Better Javascript conflict prevention ([#812](https://github.com/xwp/stream/pull/812))
85
+ * Tweak: Minor styling updates. It's about attention to detail. ([#826](https://github.com/xwp/stream/pull/826))
86
+ * Fix: Gravity Forms error when adding a note ([#811](https://github.com/xwp/stream/pull/811))
87
+ * Fix: In some instances, custom roles weren't being logged by Stream ([#824](https://github.com/xwp/stream/pull/824))
88
+ * Fix: The Customiser fix you've been waiting for! Stream now properly records changes made from the Customiser. ([#827](https://github.com/xwp/stream/pull/827))
89
+
90
+ 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)
91
+
92
  = 3.0.4 - November 27, 2015 =
93
 
94
  * Tweak: Better descriptions when a post changes status ([0eada10](https://github.com/xwp/stream/commit/0eada108b443ed3b6f9bdae3f1e4c87c77128a0a))
97
  * Fix: Logging in / out of a Multisite install is now possible ([#801](https://github.com/xwp/stream/pull/801))
98
  * Fix: The Settings connector now works with WP CLI ([78a56b2](https://github.com/xwp/stream/commit/78a56b2c6b33b4f41c7b4f1f256a4d03ad42b2cb))
99
 
100
+ Props [@lukecarbis](https://github.com/lukecarbis)
101
+
102
  = 3.0.3 - November 6, 2015 =
103
 
104
  * Tweak: Better compatibility with upcoming WordPress 4.4 ([2b2493c](https://github.com/xwp/stream/commit/2b2493ccb3ef6cba5aeb773433fdb5f0d414e8f3))
110
  * Fix: Internet Explorer 8 fix!! IE8!? Come on, people, it's 2015. ([#789](https://github.com/xwp/stream/pull/789))
111
  * Fix: EDD connector bug ([#790](https://github.com/xwp/stream/pull/790))
112
 
113
+ Props [@lukecarbis](https://github.com/lukecarbis), [@rob](https://github.com/rob), [greguly](https://github.com/greguly)
114
+
115
  = 3.0.2 - October 2, 2015 =
116
 
117
  * Tweak: Helper function for running Stream queries added ([#774](https://github.com/xwp/stream/pull/774))
124
  * Fix: Jetpack is now able to connect without error while Stream is active ([#768](https://github.com/xwp/stream/pull/768))
125
  * Fix: Reset Filters text no longer wraps to a second line ([#765](https://github.com/xwp/stream/pull/765))
126
 
127
+ Props [@lukecarbis](https://github.com/lukecarbis), Props [@sirjonathan](https://github.com/sirjonathan)
128
 
129
  = 3.0.1 - September 2, 2015 =
130
 
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.4
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.5
7
  * Author: XWP
8
  * Author URI: https://xwp.co/
9
  * License: GPLv2+
tests/testcase.php CHANGED
@@ -49,7 +49,7 @@ class WP_StreamTestCase extends \WP_UnitTestCase {
49
  $priority = isset( $test[3] ) ? $test[3] : 10;
50
 
51
  //Default function call
52
- $function_call = ( in_array( $function_call, array( 'has_action', 'has_filter' ) ) ) ? $function_call : 'has_action';
53
 
54
  //Run assertion here
55
  $this->assertEquals(
49
  $priority = isset( $test[3] ) ? $test[3] : 10;
50
 
51
  //Default function call
52
+ $function_call = ( in_array( $function_call, array( 'has_action', 'has_filter' ), true ) ) ? $function_call : 'has_action';
53
 
54
  //Run assertion here
55
  $this->assertEquals(
tests/tests/test-class-admin.php CHANGED
@@ -44,9 +44,11 @@ class Test_Admin extends WP_StreamTestCase {
44
  $this->admin->init();
45
  $this->assertNotEmpty( $this->admin->network );
46
  $this->assertNotEmpty( $this->admin->live_update );
 
47
 
48
  $this->assertInstanceOf( '\WP_Stream\Network', $this->admin->network );
49
  $this->assertInstanceOf( '\WP_Stream\Live_Update', $this->admin->live_update );
 
50
  }
51
 
52
  public function test_prepare_admin_notices() {
@@ -159,9 +161,9 @@ class Test_Admin extends WP_StreamTestCase {
159
  // Stream screen
160
  $this->admin->admin_enqueue_scripts( $this->plugin->admin->screen_id['main'] );
161
 
162
- $this->assertArrayHasKey( 'select2', $wp_scripts->registered );
163
- $this->assertArrayHasKey( 'timeago', $wp_scripts->registered );
164
- $this->assertArrayHasKey( 'timeago-locale', $wp_scripts->registered );
165
 
166
  $this->assertArrayHasKey( 'wp-stream-admin', $wp_scripts->registered );
167
  $this->assertArrayHasKey( 'wp-stream-live-updates', $wp_scripts->registered );
@@ -448,7 +450,7 @@ class Test_Admin extends WP_StreamTestCase {
448
  'blog_id' => get_current_blog_id(),
449
  'user_id' => '1',
450
  'user_role' => 'administrator',
451
- 'created' => null,
452
  'summary' => '"Hello Dave" plugin activated',
453
  'ip' => '192.168.0.1',
454
  'connector' => 'installer',
44
  $this->admin->init();
45
  $this->assertNotEmpty( $this->admin->network );
46
  $this->assertNotEmpty( $this->admin->live_update );
47
+ $this->assertNotEmpty( $this->admin->export );
48
 
49
  $this->assertInstanceOf( '\WP_Stream\Network', $this->admin->network );
50
  $this->assertInstanceOf( '\WP_Stream\Live_Update', $this->admin->live_update );
51
+ $this->assertInstanceOf( '\WP_Stream\Export', $this->admin->export );
52
  }
53
 
54
  public function test_prepare_admin_notices() {
161
  // Stream screen
162
  $this->admin->admin_enqueue_scripts( $this->plugin->admin->screen_id['main'] );
163
 
164
+ $this->assertArrayHasKey( 'wp-stream-select2', $wp_scripts->registered );
165
+ $this->assertArrayHasKey( 'wp-stream-timeago', $wp_scripts->registered );
166
+ $this->assertArrayHasKey( 'wp-stream-timeago-locale', $wp_scripts->registered );
167
 
168
  $this->assertArrayHasKey( 'wp-stream-admin', $wp_scripts->registered );
169
  $this->assertArrayHasKey( 'wp-stream-live-updates', $wp_scripts->registered );
450
  'blog_id' => get_current_blog_id(),
451
  'user_id' => '1',
452
  'user_role' => 'administrator',
453
+ 'created' => date( 'Y-m-d H:i:s' ),
454
  'summary' => '"Hello Dave" plugin activated',
455
  'ip' => '192.168.0.1',
456
  'connector' => 'installer',
tests/tests/test-class-export.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Test_Export extends WP_StreamTestCase {
5
+ /**
6
+ * Holds the export base class
7
+ *
8
+ * @var Export
9
+ */
10
+ protected $export;
11
+
12
+ /**
13
+ * Set up for tests
14
+ */
15
+ public function setUp() {
16
+ parent::setUp();
17
+ $_GET['page'] = 'wp_stream';
18
+ $this->export = new Export( $this->plugin );
19
+ $this->assertNotEmpty( $this->export );
20
+ $this->assertNotEmpty( $this->export->get_exporters() );
21
+ }
22
+
23
+ /**
24
+ * Test class constructor
25
+ */
26
+ public function test_construct() {
27
+ $this->assertNotEmpty( $this->export->plugin );
28
+
29
+ $_GET['page'] = 'not_wp_stream';
30
+ $dummy_export = new Export( $this->plugin );
31
+ $this->assertEmpty( $dummy_export->get_exporters() );
32
+ }
33
+
34
+ /**
35
+ * Test that render download uses selected renderer
36
+ */
37
+ public function test_render_download() {
38
+ $_GET['record-actions'] = 'export-csv';
39
+
40
+ ob_start();
41
+ $this->export->render_download();
42
+ $output = ob_get_clean();
43
+
44
+ $this->assertNotEmpty( $output );
45
+ $this->assertStringStartsWith( 'Date,Summary,User,Connector,Context,Action,IP Address', $output );
46
+
47
+ unset( $_GET['action'] );
48
+ }
49
+
50
+ /**
51
+ * Test no output on normal page load
52
+ */
53
+ public function test_render_output_blank() {
54
+ ob_start();
55
+ $this->export->render_download();
56
+ $output = ob_get_clean();
57
+
58
+ $this->assertEmpty( $output );
59
+ }
60
+
61
+ /**
62
+ * Test that record building grab correct columns
63
+ */
64
+ public function test_build_record() {
65
+ $columns = array( 'connector' => '' );
66
+ $data = (object) $this->dummy_stream_data();
67
+ $output = $this->export->build_record( $data, $columns );
68
+
69
+ $this->assertNotEmpty( $output );
70
+ $this->assertArrayHasKey( 'connector', $output );
71
+ $this->assertEquals( $data->connector, $output['connector'] );
72
+
73
+ $columns = array( 'context' => '' );
74
+ $output = $this->export->build_record( $data, $columns );
75
+
76
+ $this->assertNotEmpty( $output );
77
+ $this->assertArrayNotHasKey( 'connector', $output );
78
+ $this->assertArrayHasKey( 'context', $output );
79
+ $this->assertEquals( $data->context, $output['context'] );
80
+ }
81
+
82
+ /**
83
+ * Test pagination limit is increased
84
+ */
85
+ public function test_disable_paginate() {
86
+ $limit = $this->export->disable_paginate( 0 );
87
+ $this->assertEquals( $limit, 10000 );
88
+ }
89
+
90
+ /**
91
+ * Test for present columns returning
92
+ */
93
+ public function test_expand_columns() {
94
+ $test_data = array(
95
+ 'date' => '',
96
+ 'summary' => '',
97
+ 'user_id' => '',
98
+ 'context' => '',
99
+ 'action' => '',
100
+ 'ip' => '',
101
+ );
102
+ $columns = $this->export->expand_columns( $test_data );
103
+
104
+ $this->assertArrayHasKey( 'date', $columns );
105
+ $this->assertArrayHasKey( 'summary', $columns );
106
+ $this->assertArrayHasKey( 'user_id', $columns );
107
+ $this->assertArrayHasKey( 'connector', $columns );
108
+ $this->assertArrayHasKey( 'context', $columns );
109
+ $this->assertArrayHasKey( 'action', $columns );
110
+ $this->assertArrayHasKey( 'ip', $columns );
111
+ }
112
+
113
+ /**
114
+ * Test registering exporters.
115
+ */
116
+ public function test_register_exporters() {
117
+ $_GET['page'] = 'not_wp_stream';
118
+ $this->export = new Export( $this->plugin );
119
+ $this->assertEmpty( $this->export->get_exporters() );
120
+
121
+ $this->export->register_exporters();
122
+
123
+ $this->assertNotEmpty( $this->export->get_exporters() );
124
+ $this->assertArrayHasKey( 'json', $this->export->get_exporters() );
125
+ $this->assertArrayHasKey( 'csv', $this->export->get_exporters() );
126
+ }
127
+
128
+ /**
129
+ * Test registering a invalid class type produces an error
130
+ *
131
+ * @expectedException PHPUnit_Framework_Error
132
+ */
133
+ public function test_register_exporter_invalid_class() {
134
+ add_filter( 'wp_stream_exporters', function( $exporters ) {
135
+ $exporters['test'] = new \stdClass;
136
+ remove_all_filters( 'wp_stream_exporters' );
137
+ return $exporters;
138
+ });
139
+
140
+ $this->export->register_exporters();
141
+ }
142
+
143
+ /**
144
+ * Test exporter validation
145
+ */
146
+ public function test_is_valid_exporter() {
147
+ $exporters = $this->export->get_exporters();
148
+ $this->assertArrayHasKey( 'json', $exporters );
149
+ $this->assertTrue( $this->export->is_valid_exporter( $exporters['json'] ) );
150
+ }
151
+
152
+ /**
153
+ * Test exporter validation produces false
154
+ */
155
+ public function test_is_not_valid_exporter() {
156
+ $this->assertFalse( $this->export->is_valid_exporter( new \stdClass ) );
157
+ }
158
+
159
+ /**
160
+ * Return dummy stream data
161
+ */
162
+ private function dummy_stream_data() {
163
+ return array(
164
+ 'object_id' => null,
165
+ 'site_id' => '1',
166
+ 'blog_id' => get_current_blog_id(),
167
+ 'user_id' => '1',
168
+ 'user_role' => 'administrator',
169
+ 'created' => date( 'Y-m-d H:i:s' ),
170
+ 'summary' => '"Hello Dave" plugin activated',
171
+ 'ip' => '192.168.0.1',
172
+ 'connector' => 'installer',
173
+ 'context' => 'plugins',
174
+ 'action' => 'activated',
175
+ );
176
+ }
177
+ }
tests/tests/test-class-exporter-csv.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Test_Exporter_CSV extends WP_StreamTestCase {
5
+ /**
6
+ * Holds the export csv class
7
+ *
8
+ * @var Exporter_CSV
9
+ */
10
+ protected $csv_exporter;
11
+
12
+ /**
13
+ * Set up for tests
14
+ */
15
+ public function setUp() {
16
+ parent::setUp();
17
+ $_GET['page'] = 'wp_stream';
18
+
19
+ $this->plugin->admin->export->register_exporters();
20
+ $exporters = $this->plugin->admin->export->get_exporters();
21
+
22
+ $this->assertNotEmpty( $exporters );
23
+ $this->assertArrayHasKey( 'csv', $exporters );
24
+ $this->csv_exporter = $exporters['csv'];
25
+ }
26
+
27
+ /**
28
+ * Test CSV exporter output
29
+ */
30
+ public function test_output_file() {
31
+ $array = array( array( 'key' => 'value', 'key2' => 'value2' ) );
32
+ $columns = array( 'key' => 'Key', 'key2' => 'Key2' );
33
+
34
+ $this->expectOutputString( "Key,Key2\nvalue,value2\n" );
35
+ $this->csv_exporter->output_file( $array, $columns );
36
+ }
37
+ }
tests/tests/test-class-exporter-json.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ namespace WP_Stream;
3
+
4
+ class Test_Exporter_JSON extends WP_StreamTestCase {
5
+ /**
6
+ * Holds the export json class
7
+ *
8
+ * @var Exporter_JSON
9
+ */
10
+ protected $json_exporter;
11
+
12
+ /**
13
+ * Set up for tests
14
+ */
15
+ public function setUp() {
16
+ parent::setUp();
17
+ $_GET['page'] = 'wp_stream';
18
+
19
+ $this->plugin->admin->export->register_exporters();
20
+ $exporters = $this->plugin->admin->export->get_exporters();
21
+
22
+ $this->assertNotEmpty( $exporters );
23
+ $this->assertArrayHasKey( 'json', $exporters );
24
+ $this->json_exporter = $exporters['json'];
25
+ }
26
+
27
+ /**
28
+ * Test JSON exporter output
29
+ */
30
+ public function test_output_file() {
31
+ $array = array( 'key' => 'value' );
32
+ $this->expectOutputString( '{"key":"value"}' );
33
+ $this->json_exporter->output_file( $array, array() );
34
+ }
35
+ }
ui/css/admin.css CHANGED
@@ -50,30 +50,58 @@
50
  width: 12%;
51
  }
52
 
53
- .toplevel_page_wp_stream .manage-column.column-date {
54
- width: 10%;
 
55
  }
56
 
57
- .toplevel_page_wp_stream .manage-column.column-user_id {
 
 
 
 
58
  width: 18%;
59
  }
60
 
61
- .toplevel_page_wp_stream .manage-column.column-summary {
62
  width: auto;
63
  }
64
 
 
 
 
 
65
  .toplevel_page_wp_stream .stream-filter-object-id {
66
  padding-left: 5px;
67
  visibility: hidden;
68
  }
 
69
  .toplevel_page_wp_stream td.summary:hover .stream-filter-object-id {
70
  visibility: visible;
71
  }
72
 
 
 
 
 
 
 
 
 
 
 
73
  @media only screen and (min-width: 782px) {
74
  .toplevel_page_wp_stream .tablenav .tablenav-pages {
75
  margin-bottom: 4px;
76
  }
 
 
 
 
 
 
 
 
77
  }
78
 
79
  @media only screen and (max-width: 782px) {
@@ -85,13 +113,13 @@
85
  @media only screen and (max-width: 900px) {
86
  .toplevel_page_wp_stream .fixed .manage-column,
87
  .toplevel_page_wp_stream .fixed tbody tr td {
88
- display: none;
89
  }
90
  .toplevel_page_wp_stream .fixed .column-date,
91
  .toplevel_page_wp_stream .fixed .column-summary,
92
  .toplevel_page_wp_stream .fixed .column-user_id,
93
  .toplevel_page_wp_stream .fixed tbody tr.no-items td {
94
- display: table-cell;
95
  }
96
  .toplevel_page_wp_stream .fixed .column-date {
97
  width: 100px;
@@ -110,6 +138,10 @@
110
  }
111
  }
112
 
 
 
 
 
113
  .toplevel_page_wp_stream .column-user_id a {
114
  vertical-align: top;
115
  }
@@ -192,12 +224,6 @@
192
 
193
  /* Settings */
194
 
195
- .wp_stream_settings .nav-tab-wrapper a:not(.nav-tab-active),
196
- .wp_stream_network_settings .nav-tab-wrapper a:not(.nav-tab-active)
197
- .wp_stream_default_settings .nav-tab-wrapper a:not(.nav-tab-active) {
198
- border-bottom: 1px solid #ccc;
199
- }
200
-
201
  .wp_stream_settings .select2-container,
202
  .wp_stream_network_settings .select2-container,
203
  .wp_stream_default_settings .select2-container {
50
  width: 12%;
51
  }
52
 
53
+ .toplevel_page_wp_stream .column-date {
54
+ min-width: 10%;
55
+ white-space: nowrap;
56
  }
57
 
58
+ .toplevel_page_wp_stream .column-date .timeago {
59
+ padding-right: 1em;
60
+ }
61
+
62
+ .toplevel_page_wp_stream .column-user_id {
63
  width: 18%;
64
  }
65
 
66
+ .toplevel_page_wp_stream .column-summary {
67
  width: auto;
68
  }
69
 
70
+ .toplevel_page_wp_stream .column-ip {
71
+ white-space: nowrap;
72
+ }
73
+
74
  .toplevel_page_wp_stream .stream-filter-object-id {
75
  padding-left: 5px;
76
  visibility: hidden;
77
  }
78
+
79
  .toplevel_page_wp_stream td.summary:hover .stream-filter-object-id {
80
  visibility: visible;
81
  }
82
 
83
+ .toplevel_page_wp_stream .tablenav .stream-export-tablenav {
84
+ margin-top: 6px;
85
+ height: 28px;
86
+ float: right;
87
+ }
88
+
89
+ .toplevel_page_wp_stream .tablenav .stream-export-tablenav a {
90
+ margin-top: 0;
91
+ }
92
+
93
  @media only screen and (min-width: 782px) {
94
  .toplevel_page_wp_stream .tablenav .tablenav-pages {
95
  margin-bottom: 4px;
96
  }
97
+ .toplevel_page_wp_stream .tablenav .stream-export-tablenav {
98
+ margin-bottom: 4px;
99
+ float: left;
100
+ }
101
+ .toplevel_page_wp_stream .tablenav .stream-export-tablenav a {
102
+ margin-top: 5px;
103
+ display: inline-block;
104
+ }
105
  }
106
 
107
  @media only screen and (max-width: 782px) {
113
  @media only screen and (max-width: 900px) {
114
  .toplevel_page_wp_stream .fixed .manage-column,
115
  .toplevel_page_wp_stream .fixed tbody tr td {
116
+ display: none !important;
117
  }
118
  .toplevel_page_wp_stream .fixed .column-date,
119
  .toplevel_page_wp_stream .fixed .column-summary,
120
  .toplevel_page_wp_stream .fixed .column-user_id,
121
  .toplevel_page_wp_stream .fixed tbody tr.no-items td {
122
+ display: table-cell !important;
123
  }
124
  .toplevel_page_wp_stream .fixed .column-date {
125
  width: 100px;
138
  }
139
  }
140
 
141
+ .toplevel_page_wp_stream .wp-list-table tr td::before {
142
+ content: "" !important;
143
+ }
144
+
145
  .toplevel_page_wp_stream .column-user_id a {
146
  vertical-align: top;
147
  }
224
 
225
  /* Settings */
226
 
 
 
 
 
 
 
227
  .wp_stream_settings .select2-container,
228
  .wp_stream_network_settings .select2-container,
229
  .wp_stream_default_settings .select2-container {
ui/js/admin.js CHANGED
@@ -109,12 +109,22 @@ jQuery( function( $ ) {
109
  $contextInput.select2( 'val', 'group-' + $queryVars.connector );
110
  }
111
 
 
 
 
 
 
112
  $( '#record-filter-form' ).submit( function() {
113
  var $context = $( '.toplevel_page_wp_stream :input.chosen-select[name="context"]' ),
114
  $option = $context.find( 'option:selected' ),
115
  $connector = $context.parent().find( '.record-filter-connector' ),
116
  optionConnector = $option.data( 'group' ),
117
- optionClass = $option.prop( 'class' );
 
 
 
 
 
118
 
119
  $connector.val( optionConnector );
120
 
109
  $contextInput.select2( 'val', 'group-' + $queryVars.connector );
110
  }
111
 
112
+ $( 'input[type=submit]', '#record-filter-form' ).click( function() {
113
+ $( 'input[type=submit]', $( this ).parents( 'form' )).removeAttr( 'clicked' );
114
+ $( this ).attr( 'clicked', 'true' );
115
+ });
116
+
117
  $( '#record-filter-form' ).submit( function() {
118
  var $context = $( '.toplevel_page_wp_stream :input.chosen-select[name="context"]' ),
119
  $option = $context.find( 'option:selected' ),
120
  $connector = $context.parent().find( '.record-filter-connector' ),
121
  optionConnector = $option.data( 'group' ),
122
+ optionClass = $option.prop( 'class' ),
123
+ $recordAction = $( '.recordactions select' );
124
+
125
+ if ( $( '#record-actions-submit' ).attr( 'clicked' ) !== 'true' ) {
126
+ $recordAction.val( '' );
127
+ }
128
 
129
  $connector.val( optionConnector );
130