Activity Log - Version 2.4.0

Version Description

  • New! Export your Activity Log data records to CSV (#70)
Download this release

Release Info

Developer KingYes
Plugin Icon 128x128 Activity Log
Version 2.4.0
Comparing to
See all releases

Code changes from version 2.3.6 to 2.4.0

aryo-activity-log.php CHANGED
@@ -5,7 +5,7 @@ Plugin URI: http://wordpress.org/plugins/aryo-activity-log/
5
  Description: Get aware of any activities that are taking place on your dashboard! Imagine it like a black-box for your WordPress site. e.g. post was deleted, plugin was activated, user logged in or logged out - it's all these for you to see.
6
  Author: Yakir Sitbon, Maor Chasen, Ariel Klikstein
7
  Author URI: http://pojo.me/
8
- Version: 2.3.6
9
  Text Domain: aryo-activity-log
10
  License: GPLv2 or later
11
 
@@ -38,6 +38,8 @@ include( 'classes/class-aal-api.php' );
38
  include( 'classes/class-aal-hooks.php' );
39
  include( 'classes/class-aal-notifications.php' );
40
  include( 'classes/class-aal-help.php' );
 
 
41
 
42
  // Integrations
43
  include( 'classes/class-aal-integration-woocommerce.php' );
@@ -94,6 +96,7 @@ final class AAL_Main {
94
  $this->api = new AAL_API();
95
  $this->notifications = new AAL_Notifications();
96
  $this->help = new AAL_Help();
 
97
 
98
  // set up our DB name
99
  $wpdb->activity_log = $wpdb->prefix . 'aryo_activity_log';
5
  Description: Get aware of any activities that are taking place on your dashboard! Imagine it like a black-box for your WordPress site. e.g. post was deleted, plugin was activated, user logged in or logged out - it's all these for you to see.
6
  Author: Yakir Sitbon, Maor Chasen, Ariel Klikstein
7
  Author URI: http://pojo.me/
8
+ Version: 2.4.0
9
  Text Domain: aryo-activity-log
10
  License: GPLv2 or later
11
 
38
  include( 'classes/class-aal-hooks.php' );
39
  include( 'classes/class-aal-notifications.php' );
40
  include( 'classes/class-aal-help.php' );
41
+ include( 'classes/class-aal-export.php' );
42
+ include( 'classes/abstract-class-aal-exporter.php' );
43
 
44
  // Integrations
45
  include( 'classes/class-aal-integration-woocommerce.php' );
96
  $this->api = new AAL_API();
97
  $this->notifications = new AAL_Notifications();
98
  $this->help = new AAL_Help();
99
+ $this->export = new AAL_Export();
100
 
101
  // set up our DB name
102
  $wpdb->activity_log = $wpdb->prefix . 'aryo_activity_log';
classes/abstract-class-aal-exporter.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ abstract class AAL_Exporter {
5
+ /**
6
+ * Exporter name
7
+ *
8
+ * @var string
9
+ */
10
+ public $name;
11
+
12
+ /**
13
+ * Exporter unique identifier
14
+ *
15
+ * @var string
16
+ */
17
+ public $id;
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
+ abstract public function write( $data, $columns );
27
+ }
classes/class-aal-activity-log-list-table.php CHANGED
@@ -71,7 +71,7 @@ class AAL_Activity_Log_List_Table extends WP_List_Table {
71
  return 'AND (' . implode( ' OR ', $where ) . ') AND (' . implode( ' OR ', $where_caps ) . ')';
72
  }
73
 
74
- protected function _get_action_label( $action ) {
75
  return ucwords( str_replace( '_', ' ', __( $action, 'aryo-activity-log' ) ) );
76
  }
77
 
@@ -141,7 +141,7 @@ class AAL_Activity_Log_List_Table extends WP_List_Table {
141
 
142
  switch ( $column_name ) {
143
  case 'action' :
144
- $return = $this->_get_action_label( $item->action );
145
  break;
146
  case 'date' :
147
  $return = sprintf( '<strong>' . __( '%s ago', 'aryo-activity-log' ) . '</strong>', human_time_diff( $item->hist_time, current_time( 'timestamp' ) ) );
@@ -241,9 +241,9 @@ class AAL_Activity_Log_List_Table extends WP_List_Table {
241
  }
242
 
243
  public function display_tablenav( $which ) {
244
- if ( 'top' == $which )
245
  $this->search_box( __( 'Search', 'aryo-activity-log' ), 'aal-search' );
246
- wp_nonce_field( 'bulk-' . $this->_args['plural'] );
247
  ?>
248
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
249
  <?php
@@ -254,9 +254,52 @@ class AAL_Activity_Log_List_Table extends WP_List_Table {
254
  </div>
255
  <?php
256
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
 
258
  public function extra_tablenav( $which ) {
259
  global $wpdb;
 
 
 
 
260
 
261
  if ( 'top' !== $which )
262
  return;
@@ -298,7 +341,7 @@ class AAL_Activity_Log_List_Table extends WP_List_Table {
298
  printf( '<option value="%s"%s>%s</option>', $key, selected( $_REQUEST['dateshow'], $key, false ), $value );
299
  echo '</select>';
300
 
301
- submit_button( __( 'Filter', 'aryo-activity-log' ), 'button', false, false, array( 'id' => 'activity-query-submit' ) );
302
  }
303
 
304
  if ( $users ) {
@@ -374,7 +417,7 @@ class AAL_Activity_Log_List_Table extends WP_List_Table {
374
 
375
  $output = array();
376
  foreach ( $actions as $type )
377
- $output[] = sprintf( '<option value="%s"%s>%s</option>', $type->action, selected( $_REQUEST['showaction'], $type->action, false ), $this->_get_action_label( $type->action ) );
378
 
379
  echo '<select name="showaction" id="hs-filter-showaction">';
380
  printf( '<option value="">%s</option>', __( 'All Actions', 'aryo-activity-log' ) );
71
  return 'AND (' . implode( ' OR ', $where ) . ') AND (' . implode( ' OR ', $where_caps ) . ')';
72
  }
73
 
74
+ public function get_action_label( $action ) {
75
  return ucwords( str_replace( '_', ' ', __( $action, 'aryo-activity-log' ) ) );
76
  }
77
 
141
 
142
  switch ( $column_name ) {
143
  case 'action' :
144
+ $return = $this->get_action_label( $item->action );
145
  break;
146
  case 'date' :
147
  $return = sprintf( '<strong>' . __( '%s ago', 'aryo-activity-log' ) . '</strong>', human_time_diff( $item->hist_time, current_time( 'timestamp' ) ) );
241
  }
242
 
243
  public function display_tablenav( $which ) {
244
+ if ( 'top' == $which ) {
245
  $this->search_box( __( 'Search', 'aryo-activity-log' ), 'aal-search' );
246
+ }
247
  ?>
248
  <div class="tablenav <?php echo esc_attr( $which ); ?>">
249
  <?php
254
  </div>
255
  <?php
256
  }
257
+
258
+ public function extra_tablenav_footer() {
259
+ /**
260
+ * Filter list of record actions
261
+ *
262
+ * @return array Array items should represent action_id => 'Action Title'
263
+ */
264
+ $actions = apply_filters( 'aal_record_actions', array() );
265
+ ?>
266
+ <?php if ( count( $actions ) > 1 ) : ?>
267
+ <div class="alignleft actions recordactions">
268
+ <select name="aal-record-action">
269
+ <option value=""><?php echo esc_attr__( 'Export File Format', 'aryo-activity-log' ); ?></option>
270
+ <?php foreach ( $actions as $action_key => $action_title ) : ?>
271
+ <option value="<?php echo esc_attr( $action_key ); ?>"><?php echo esc_html( $action_title ); ?></option>
272
+ <?php endforeach; ?>
273
+ </select>
274
+ </div>
275
+ <?php else :
276
+ $action_title = reset( $actions );
277
+ $action_key = key( $actions );
278
+ ?>
279
+ <input type="hidden" name="aal-record-action" value="<?php echo esc_attr( $action_key ); ?>">
280
+ <?php endif; ?>
281
+
282
+ <button type="submit" name="aal-record-actions-submit" id="record-actions-submit" class="button button-primary" value="1">
283
+ <?php
284
+ // Is result filtering enabled?
285
+ if ( array_key_exists( 'aal-filter', $_GET ) ) {
286
+ echo sprintf( esc_html__( 'Export filtered records as %s', 'aryo-activity-log' ), $action_title );
287
+ } else {
288
+ echo sprintf( esc_html__( 'Export as %s', 'aryo-activity-log' ), $action_title );
289
+ }
290
+ ?>
291
+ </button>
292
+
293
+ <?php wp_nonce_field( 'aal_actions_nonce', 'aal_actions_nonce' ); ?>
294
+ <?php
295
+ }
296
 
297
  public function extra_tablenav( $which ) {
298
  global $wpdb;
299
+
300
+ if ( 'bottom' === $which ) {
301
+ $this->extra_tablenav_footer();
302
+ }
303
 
304
  if ( 'top' !== $which )
305
  return;
341
  printf( '<option value="%s"%s>%s</option>', $key, selected( $_REQUEST['dateshow'], $key, false ), $value );
342
  echo '</select>';
343
 
344
+ submit_button( __( 'Filter', 'aryo-activity-log' ), 'button', 'aal-filter', false, array( 'id' => 'activity-query-submit' ) );
345
  }
346
 
347
  if ( $users ) {
417
 
418
  $output = array();
419
  foreach ( $actions as $type )
420
+ $output[] = sprintf( '<option value="%s"%s>%s</option>', $type->action, selected( $_REQUEST['showaction'], $type->action, false ), $this->get_action_label( $type->action ) );
421
 
422
  echo '<select name="showaction" id="hs-filter-showaction">';
423
  printf( '<option value="">%s</option>', __( 'All Actions', 'aryo-activity-log' ) );
classes/class-aal-admin-ui.php CHANGED
@@ -33,6 +33,9 @@ class AAL_Admin_Ui {
33
 
34
  <?php // TODO: move to a separate file. ?>
35
  <style>
 
 
 
36
  .aal-pt {
37
  color: #ffffff;
38
  padding: 1px 4px;
@@ -232,8 +235,10 @@ class AAL_Admin_Ui {
232
  * @return AAL_Activity_Log_List_Table
233
  */
234
  public function get_list_table() {
235
- if ( is_null( $this->_list_table ) )
236
  $this->_list_table = new AAL_Activity_Log_List_Table( array( 'screen' => $this->_screens['main'] ) );
 
 
237
 
238
  return $this->_list_table;
239
  }
33
 
34
  <?php // TODO: move to a separate file. ?>
35
  <style>
36
+ #record-actions-submit {
37
+ margin-top: 10px;
38
+ }
39
  .aal-pt {
40
  color: #ffffff;
41
  padding: 1px 4px;
235
  * @return AAL_Activity_Log_List_Table
236
  */
237
  public function get_list_table() {
238
+ if ( is_null( $this->_list_table ) ) {
239
  $this->_list_table = new AAL_Activity_Log_List_Table( array( 'screen' => $this->_screens['main'] ) );
240
+ do_action( 'aal_admin_page_load', $this->_list_table );
241
+ }
242
 
243
  return $this->_list_table;
244
  }
classes/class-aal-export.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ class AAL_Export {
5
+ private $exporters;
6
+
7
+ public function __construct() {
8
+ add_action( 'aal_admin_page_load', array( $this, 'admin_register_exporters' ), 10 );
9
+ add_action( 'aal_admin_page_load', array( $this, 'admin_capture_action' ), 20 );
10
+
11
+ add_filter( 'aal_record_actions', array( $this, 'filter_register_actions' ) );
12
+ }
13
+
14
+ public function filter_register_actions( $actions ) {
15
+ foreach ( $this->get_exporters() as $exporter ) {
16
+ $actions[ $exporter->id ] = $exporter->name;
17
+ }
18
+ return $actions;
19
+ }
20
+
21
+ public function admin_capture_action( $list_table ) {
22
+ if ( empty( $_GET['aal-record-actions-submit'] ) ) {
23
+ return;
24
+ }
25
+
26
+ if ( empty( $_GET['aal_actions_nonce'] ) ) {
27
+ $this->redirect_back();
28
+ }
29
+
30
+ if ( empty( $_GET['aal-record-action'] ) || ! wp_verify_nonce( $_GET['aal_actions_nonce'], 'aal_actions_nonce' ) ) {
31
+ $this->redirect_back();
32
+ }
33
+
34
+ if ( isset( $_GET['page'] ) && 'activity_log_page' !== $_GET['page'] ) {
35
+ $this->redirect_back();
36
+ }
37
+
38
+ $exporter_selected = $_GET['aal-record-action'];
39
+
40
+ // If exporter doesn't exist or isn't registered, bail
41
+ if ( ! array_key_exists( $exporter_selected, $this->get_exporters() ) ) {
42
+ $this->redirect_back();
43
+ }
44
+
45
+ // Disable row limit
46
+ add_filter( 'edit_aal_logs_per_page', array( $this, 'increase_throughput' ) );
47
+
48
+ // Prep items for export
49
+ $list_table->prepare_items();
50
+ $items = $list_table->items;
51
+ $columns = $list_table->get_columns();
52
+
53
+ $op = array();
54
+ foreach ( $items as $item ) {
55
+ $op[] = $this->prep_row( $item, $columns, $list_table );
56
+ }
57
+
58
+ $exporter = $this->exporters[ $exporter_selected ];
59
+ $exporter->write( $op, $columns );
60
+ }
61
+
62
+ protected function redirect_back() {
63
+ wp_redirect( menu_page_url( 'activity_log_page', false ) );
64
+ exit;
65
+ }
66
+
67
+ /**
68
+ * @param stdClass $item
69
+ * @param array $columns
70
+ * @param AAL_Activity_Log_List_Table $list_table
71
+ *
72
+ * @return array
73
+ */
74
+ private function prep_row( $item, $columns, $list_table ) {
75
+ $row = array();
76
+
77
+ foreach ( array_keys( $columns ) as $column ) {
78
+ switch ( $column ) {
79
+ case 'date':
80
+ $created = date( 'Y-m-d H:i:s', $item->hist_time );
81
+ $row[ $column ] = get_date_from_gmt( $created, 'Y/m/d h:i:s A' );
82
+ break;
83
+
84
+ case 'author':
85
+ $user = get_userdata( $item->user_id );
86
+ $row[ $column ] = isset( $user->display_name ) ? $user->display_name : 'unknown';
87
+ break;
88
+
89
+ case 'ip':
90
+ $row[ $column ] = $item->hist_ip;
91
+ break;
92
+
93
+ case 'type':
94
+ $row[ $column ] = $item->object_type;
95
+ break;
96
+
97
+ case 'label':
98
+ $row[ $column ] = $item->object_subtype;
99
+ break;
100
+
101
+ case 'action':
102
+ $row[ $column ] = $list_table->get_action_label( $item->action );
103
+ break;
104
+
105
+ case 'description':
106
+ $row[ $column ] = $item->object_name;
107
+ break;
108
+ }
109
+ }
110
+
111
+ return $row;
112
+ }
113
+
114
+ public function admin_register_exporters() {
115
+ $builtin_exporters = array(
116
+ 'csv',
117
+ );
118
+
119
+ $exporter_instances = array();
120
+
121
+ foreach ( $builtin_exporters as $exporter ) {
122
+ include_once sprintf( '%s/exporters/%s', dirname( ACTIVITY_LOG__FILE__ ), 'class-aal-exporter-' . $exporter . '.php' );
123
+
124
+ $classname = sprintf( 'AAL_Exporter_%s', str_replace( '-', '_', $exporter ) );
125
+ if ( ! class_exists( $classname ) ) {
126
+ continue;
127
+ }
128
+
129
+ $instance = new $classname;
130
+ if ( ! property_exists( $instance, 'id' ) ) {
131
+ continue;
132
+ }
133
+
134
+ $exporter_instances[ $instance->id ] = $instance;
135
+ }
136
+
137
+ /**
138
+ * Allows for adding additional exporters via classes that extend Exporter.
139
+ *
140
+ * @param array $classes An array of Exporter objects. In the format exporter_slug => Exporter_Class()
141
+ */
142
+ $this->exporters = apply_filters( 'aal_exporters', $exporter_instances );
143
+ }
144
+
145
+ /**
146
+ * Returns an array with all available exporters
147
+ *
148
+ * @return array
149
+ */
150
+ private function get_exporters() {
151
+ return $this->exporters;
152
+ }
153
+
154
+ /**
155
+ * Increase throughput
156
+ *
157
+ * @param int $records_per_page Old limit of records
158
+ *
159
+ * @return int
160
+ */
161
+ public function increase_throughput( $records_per_page ) {
162
+ return PHP_INT_MAX;
163
+ }
164
+ }
classes/class-aal-settings.php CHANGED
@@ -23,7 +23,7 @@ class AAL_Settings {
23
  }
24
 
25
  public function plugin_action_links( $links ) {
26
- $settings_link = sprintf( '<a href="%s" target="_blank">%s</a>', 'https://github.com/KingYes/wordpress-aryo-activity-log', __( 'GitHub', 'aryo-activity-log' ) );
27
  array_unshift( $links, $settings_link );
28
 
29
  $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=activity-log-settings' ), __( 'Settings', 'aryo-activity-log' ) );
23
  }
24
 
25
  public function plugin_action_links( $links ) {
26
+ $settings_link = sprintf( '<a href="%s" target="_blank">%s</a>', 'https://github.com/pojome/wordpress-aryo-activity-log', __( 'GitHub', 'aryo-activity-log' ) );
27
  array_unshift( $links, $settings_link );
28
 
29
  $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'admin.php?page=activity-log-settings' ), __( 'Settings', 'aryo-activity-log' ) );
exporters/class-aal-exporter-csv.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ class AAL_Exporter_csv {
5
+ /**
6
+ * Exporter name
7
+ *
8
+ * @var string
9
+ */
10
+ public $name = 'CSV';
11
+
12
+ /**
13
+ * Exporter ID
14
+ *
15
+ * @var string
16
+ */
17
+ public $id = 'csv';
18
+
19
+ /**
20
+ * Writes 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 write( $data, $columns ) {
27
+ $is_test_mode_off = ! defined( 'AAL_TESTMODE' ) || ( defined( 'AAL_TESTMODE' ) && ! AAL_TESTMODE );
28
+
29
+ if ( $is_test_mode_off ) {
30
+ header( 'Content-type: text/csv' );
31
+ header( 'Content-Disposition: attachment; filename="activity-log-export.csv"' );
32
+ }
33
+
34
+ $output = join( ',', array_values( $columns ) ) . "\n";
35
+ foreach ( $data as $row ) {
36
+ $output .= join( ',', $row ) . "\n";
37
+ }
38
+
39
+ echo $output; // @codingStandardsIgnoreLine text-only output
40
+
41
+ if ( $is_test_mode_off ) {
42
+ exit;
43
+ }
44
+ }
45
+ }
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: pojo.me, KingYes, ariel.k, maor
3
  Tags: automation, actions, activity, Activity Log, admin, admin actions, administration, analytics, audit, audit log, audit logs, bbPress, changes, dashboard, email notifications, event, event log, log, logger, Logs, monitor, multi-users, multisite, notifications, security, security audit trail, security event log, stats, stream, tracking, troubleshooting, user activity report, user tracking, woocommerce, bbPress
4
  Requires at least: 4.4
5
- Tested up to: 4.8
6
- Stable tag: 2.3.6
7
  License: GPLv2 or later
8
 
9
  The #1 Activity Log plugin helps you monitor & log all changes and activities on your site, so you can run a safer, more organized WordPress site.
@@ -46,6 +46,8 @@ If you have tens of users or more, you really can’t know who did what. This pl
46
 
47
  <strong>New!</strong> You are now able to get email notifications once an event you have defined (via rules) has occurred. This is useful in cases you must know right away when someone does something on your site. We use it to thwart hacker attempts, for example.
48
 
 
 
49
  <h4>What people are saying</h4>
50
  * <em>“Best 10 Free WordPress Plugins of the Month – July 2014: Keeping tabs on what your users do with their access to the Dashboard”</em> - [ManageWp.com](https://managewp.com/best-free-wordpress-plugins-july-2014)
51
  * <em>“Thanks to this step, we’ve discovered that our site was undergoing a brute force attack”</em> - [artdriver.com](http://www.artdriver.com/wordpress-site-hacked-solution-time/)
@@ -97,9 +99,15 @@ Would you like to like to contribute to Activity Log? You are more than welcome
97
 
98
  * This plugin is released under a GPL license.
99
 
 
 
 
100
 
101
  == Changelog ==
102
 
 
 
 
103
  = 2.3.6 =
104
  * Fix! - Admin table filters
105
 
2
  Contributors: pojo.me, KingYes, ariel.k, maor
3
  Tags: automation, actions, activity, Activity Log, admin, admin actions, administration, analytics, audit, audit log, audit logs, bbPress, changes, dashboard, email notifications, event, event log, log, logger, Logs, monitor, multi-users, multisite, notifications, security, security audit trail, security event log, stats, stream, tracking, troubleshooting, user activity report, user tracking, woocommerce, bbPress
4
  Requires at least: 4.4
5
+ Tested up to: 4.9
6
+ Stable tag: 2.4.0
7
  License: GPLv2 or later
8
 
9
  The #1 Activity Log plugin helps you monitor & log all changes and activities on your site, so you can run a safer, more organized WordPress site.
46
 
47
  <strong>New!</strong> You are now able to get email notifications once an event you have defined (via rules) has occurred. This is useful in cases you must know right away when someone does something on your site. We use it to thwart hacker attempts, for example.
48
 
49
+ <strong>New!</strong> Export your Activity Log data records to CSV. Developers can easily add support for custom data formats with our new dedicated Export API.
50
+
51
  <h4>What people are saying</h4>
52
  * <em>“Best 10 Free WordPress Plugins of the Month – July 2014: Keeping tabs on what your users do with their access to the Dashboard”</em> - [ManageWp.com](https://managewp.com/best-free-wordpress-plugins-july-2014)
53
  * <em>“Thanks to this step, we’ve discovered that our site was undergoing a brute force attack”</em> - [artdriver.com](http://www.artdriver.com/wordpress-site-hacked-solution-time/)
99
 
100
  * This plugin is released under a GPL license.
101
 
102
+ = Can I export logs? =
103
+
104
+ * You can easily export logs with Activity Log. We also support exporting filtered results. Filter by the time the action took place, roles, users, options, action type, and more.
105
 
106
  == Changelog ==
107
 
108
+ = 2.4.0 =
109
+ * New! Export your Activity Log data records to CSV ([#70](https://github.com/pojome/elementor/issues/70))
110
+
111
  = 2.3.6 =
112
  * Fix! - Admin table filters
113