Newsletter - Version 5.5.0

Version Description

  • Added IP storage control
  • Fixed a warning and a debug notice
  • Aggregated warnings on admin side
Download this release

Release Info

Developer satollo
Plugin Icon 128x128 Newsletter
Version 5.5.0
Comparing to
See all releases

Code changes from version 5.4.9 to 5.5.0

includes/module.php CHANGED
@@ -150,7 +150,7 @@ class NewsletterModule {
150
  function insert($table, $data) {
151
  global $wpdb;
152
  $this->logger->debug("inserting into table $table");
153
- $wpdb->insert($table, $data);
154
  if ($r === false) {
155
  $this->logger->fatal($wpdb->last_error);
156
  }
@@ -834,19 +834,24 @@ class NewsletterModule {
834
  function check_user() {
835
  global $wpdb;
836
 
 
 
837
  if (isset($_REQUEST['nk'])) {
838
  list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
839
  } else if (isset($_COOKIE['newsletter'])) {
840
  list ($id, $token) = @explode('-', $_COOKIE['newsletter'], 2);
841
  }
842
 
843
- $user = $this->get_user($id);
844
- if ($user == null || $token != $user->token) {
845
- $user = null;
846
- if (is_user_logged_in()) {
847
- $user = $this->get_user_by_wp_user_id(get_current_user_id());
848
  }
849
  }
 
 
 
 
850
  return $user;
851
  }
852
 
@@ -861,6 +866,9 @@ class NewsletterModule {
861
  function get_user($id_or_email, $format = OBJECT) {
862
  global $wpdb;
863
 
 
 
 
864
  // To simplify the reaload of a user passing the user it self.
865
  if (is_object($id_or_email)) {
866
  $id_or_email = $id_or_email->id;
@@ -1148,7 +1156,7 @@ class NewsletterModule {
1148
  }
1149
 
1150
  if ($email) {
1151
- if (!is_object($email)) {
1152
  $email = $this->get_email($email);
1153
  }
1154
  $params .= '&nek=' . urlencode($this->get_email_key($email));
@@ -1206,7 +1214,16 @@ class NewsletterModule {
1206
  }
1207
  $parts = explode('.', $ip);
1208
  array_pop($parts);
1209
- return implode('.', $parts);
 
 
 
 
 
 
 
 
 
1210
  }
1211
 
1212
  function anonymize_user($id) {
@@ -1267,7 +1284,9 @@ class NewsletterModule {
1267
  $data[$field_name] = $user->$field_name;
1268
  }
1269
  $data['status'] = $user->status;
1270
- $this->store->save($wpdb->prefix . 'newsletter_user_logs', array('user_id' => $user->id, 'source' => $source, 'created' => time(), 'data' => json_encode($data)));
 
 
1271
  }
1272
 
1273
  /**
150
  function insert($table, $data) {
151
  global $wpdb;
152
  $this->logger->debug("inserting into table $table");
153
+ $r = $wpdb->insert($table, $data);
154
  if ($r === false) {
155
  $this->logger->fatal($wpdb->last_error);
156
  }
834
  function check_user() {
835
  global $wpdb;
836
 
837
+ $user = null;
838
+
839
  if (isset($_REQUEST['nk'])) {
840
  list($id, $token) = @explode('-', $_REQUEST['nk'], 2);
841
  } else if (isset($_COOKIE['newsletter'])) {
842
  list ($id, $token) = @explode('-', $_COOKIE['newsletter'], 2);
843
  }
844
 
845
+ if (isset($id)) {
846
+ $user = $this->get_user($id);
847
+ if ($token != $user->token) {
848
+ $user = null;
 
849
  }
850
  }
851
+
852
+ if ($user == null && is_user_logged_in()) {
853
+ $user = $this->get_user_by_wp_user_id(get_current_user_id());
854
+ }
855
  return $user;
856
  }
857
 
866
  function get_user($id_or_email, $format = OBJECT) {
867
  global $wpdb;
868
 
869
+ if (empty($id_or_email))
870
+ return null;
871
+
872
  // To simplify the reaload of a user passing the user it self.
873
  if (is_object($id_or_email)) {
874
  $id_or_email = $id_or_email->id;
1156
  }
1157
 
1158
  if ($email) {
1159
+ if (!is_object($email)) {
1160
  $email = $this->get_email($email);
1161
  }
1162
  $params .= '&nek=' . urlencode($this->get_email_key($email));
1214
  }
1215
  $parts = explode('.', $ip);
1216
  array_pop($parts);
1217
+ return implode('.', $parts) . '.0';
1218
+ }
1219
+
1220
+ function process_ip($ip) {
1221
+ $option = Newsletter::instance()->options['ip'];
1222
+ if (empty($option))
1223
+ return $ip;
1224
+ if ($option == 'anonymize')
1225
+ return $this->anonymize_ip($ip);
1226
+ return '';
1227
  }
1228
 
1229
  function anonymize_user($id) {
1284
  $data[$field_name] = $user->$field_name;
1285
  }
1286
  $data['status'] = $user->status;
1287
+ $ip = $this->get_remote_ip();
1288
+ $ip = $this->process_ip($ip);
1289
+ $this->store->save($wpdb->prefix . 'newsletter_user_logs', array('ip' => $ip, 'user_id' => $user->id, 'source' => $source, 'created' => time(), 'data' => json_encode($data)));
1290
  }
1291
 
1292
  /**
main/defaults.php CHANGED
@@ -18,6 +18,7 @@ $options = array(
18
  'track'=>1,
19
  'css'=>'',
20
  'css_disabled'=>0,
 
21
 
22
  'header_logo' => '',
23
  'header_title' => get_bloginfo('name'),
18
  'track'=>1,
19
  'css'=>'',
20
  'css_disabled'=>0,
21
+ 'ip'=>'',
22
 
23
  'header_logo' => '',
24
  'header_title' => get_bloginfo('name'),
main/main.php CHANGED
@@ -275,6 +275,13 @@ if (!empty($return_path)) {
275
  <?php $controls->log_level('log_level'); ?>
276
  </td>
277
  </tr>
 
 
 
 
 
 
 
278
 
279
  <tr>
280
  <th><?php _e('Newsletters tracking default', 'newsletter') ?></th>
275
  <?php $controls->log_level('log_level'); ?>
276
  </td>
277
  </tr>
278
+
279
+ <tr>
280
+ <th><?php _e('IP addresses', 'newsletter')?></th>
281
+ <td>
282
+ <?php $controls->select('ip', array(''=>__('Store', 'newsletter'), 'anonymize'=> __('Anonymize', 'newsletter'), 'skip'=>__('Do not store', 'newsletter'))); ?>
283
+ </td>
284
+ </tr>
285
 
286
  <tr>
287
  <th><?php _e('Newsletters tracking default', 'newsletter') ?></th>
plugin.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
- Version: 5.4.9
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
@@ -14,11 +14,9 @@
14
  */
15
 
16
  // Used as dummy parameter on css and js links
17
- define('NEWSLETTER_VERSION', '5.4.9');
18
 
19
- global $wpdb, $newsletter;
20
-
21
- //@include_once WP_CONTENT_DIR . '/extensions/newsletter/config.php';
22
 
23
  if (!defined('NEWSLETTER_EMAILS_TABLE'))
24
  define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
@@ -35,8 +33,8 @@ if (!defined('NEWSLETTER_SENT_TABLE'))
35
  // Do not use basename(dirname()) since on activation the plugin is sandboxed inside a function
36
  define('NEWSLETTER_SLUG', 'newsletter');
37
 
38
- define('NEWSLETTER_DIR', WP_PLUGIN_DIR . '/' . NEWSLETTER_SLUG);
39
- define('NEWSLETTER_INCLUDES_DIR', WP_PLUGIN_DIR . '/' . NEWSLETTER_SLUG . '/includes');
40
 
41
  // Almost obsolete but the first two must be kept for compatibility with modules
42
  define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
@@ -126,7 +124,7 @@ class Newsletter extends NewsletterModule {
126
 
127
  // Here because the upgrade is called by the parent constructor and uses the scheduler
128
  add_filter('cron_schedules', array($this, 'hook_cron_schedules'), 1000);
129
- parent::__construct('main', '1.4.7', null, array('info', 'smtp'));
130
 
131
  $max = $this->options['scheduler_max'];
132
  if (!is_numeric($max)) {
@@ -186,6 +184,8 @@ class Newsletter extends NewsletterModule {
186
  }
187
  $mean = $mean / count($calls) - 1;
188
  update_option('newsletter_diagnostic_cron_data', array('mean' => $mean, 'max' => $max, 'min' => $min), false);
 
 
189
  }
190
  }
191
  }
@@ -344,26 +344,9 @@ class Newsletter extends NewsletterModule {
344
  * if there are no warnings.
345
  */
346
  function warnings() {
347
- $warnings = '';
348
- $x = wp_next_scheduled('newsletter');
349
- if ($x === false) {
350
- $warnings .= 'The delivery engine is off (it should never be off). Deactivate and reactivate the plugin. Thank you.<br>';
351
- } else if (time() - $x > 900) {
352
- $warnings .= 'The cron system seems not running correctly. See <a href="https://www.thenewsletterplugin.com/how-to-make-the-wordpress-cron-work" target="_blank">this page</a> for more information.<br>';
353
- } else {
354
- $cron_data = get_option('newsletter_diagnostic_cron_data');
355
- if ($cron_data && $cron_data['mean'] > 400) {
356
- $warnings .= 'The WordPress internal schedule is not triggered enough often. See <a href="https://www.thenewsletterplugin.com/how-to-make-the-wordpress-cron-work" target="_blank">this page</a> for more information.<br>';
357
- }
358
- }
359
-
360
- if (!empty($warnings)) {
361
- echo '<div class="tnp-error">';
362
- echo $warnings;
363
- echo '</div>';
364
- }
365
  }
366
-
367
  function hook_init() {
368
  global $cache_stop, $hyper_cache_stop, $wpdb;
369
 
@@ -437,16 +420,27 @@ class Newsletter extends NewsletterModule {
437
  // Check of Newsletter dedicated page
438
  if ($this->options['page']) {
439
  if (get_post_status($this->options['page']) !== 'publish') {
440
-
441
- echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit">Edit the page</a>.</p></div>';
442
  } else {
443
  $content = get_post_field('post_content', $this->options['page']);
444
  // With and without attributes
445
  if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
446
- echo '<div class="notice notice-error"><p>The Newsletter dedicated page does not contain the [newsletter] shortcode. <a href="', site_url('/wp-admin/post.php'), '?post=', $this->options['page'], '&action=edit">Edit the page</a>.</p></div>';
447
  }
448
  }
449
  }
 
 
 
 
 
 
 
 
 
 
 
 
450
  }
451
 
452
  function hook_wp_enqueue_scripts() {
4
  Plugin Name: Newsletter
5
  Plugin URI: https://www.thenewsletterplugin.com/plugins/newsletter
6
  Description: Newsletter is a cool plugin to create your own subscriber list, to send newsletters, to build your business. <strong>Before update give a look to <a href="https://www.thenewsletterplugin.com/category/release">this page</a> to know what's changed.</strong>
7
+ Version: 5.5.0
8
  Author: Stefano Lissa & The Newsletter Team
9
  Author URI: https://www.thenewsletterplugin.com
10
  Disclaimer: Use at your own risk. No warranty expressed or implied is provided.
14
  */
15
 
16
  // Used as dummy parameter on css and js links
17
+ define('NEWSLETTER_VERSION', '5.5.0');
18
 
19
+ global $newsletter;
 
 
20
 
21
  if (!defined('NEWSLETTER_EMAILS_TABLE'))
22
  define('NEWSLETTER_EMAILS_TABLE', $wpdb->prefix . 'newsletter_emails');
33
  // Do not use basename(dirname()) since on activation the plugin is sandboxed inside a function
34
  define('NEWSLETTER_SLUG', 'newsletter');
35
 
36
+ define('NEWSLETTER_DIR', __DIR__);
37
+ define('NEWSLETTER_INCLUDES_DIR', __DIR__ . '/includes');
38
 
39
  // Almost obsolete but the first two must be kept for compatibility with modules
40
  define('NEWSLETTER_URL', WP_PLUGIN_URL . '/newsletter');
124
 
125
  // Here because the upgrade is called by the parent constructor and uses the scheduler
126
  add_filter('cron_schedules', array($this, 'hook_cron_schedules'), 1000);
127
+ parent::__construct('main', '1.4.9', null, array('info', 'smtp'));
128
 
129
  $max = $this->options['scheduler_max'];
130
  if (!is_numeric($max)) {
184
  }
185
  $mean = $mean / count($calls) - 1;
186
  update_option('newsletter_diagnostic_cron_data', array('mean' => $mean, 'max' => $max, 'min' => $min), false);
187
+ } else {
188
+ update_option('newsletter_diagnostic_cron_data', '', false);
189
  }
190
  }
191
  }
344
  * if there are no warnings.
345
  */
346
  function warnings() {
347
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
  }
349
+
350
  function hook_init() {
351
  global $cache_stop, $hyper_cache_stop, $wpdb;
352
 
420
  // Check of Newsletter dedicated page
421
  if ($this->options['page']) {
422
  if (get_post_status($this->options['page']) !== 'publish') {
423
+ echo '<div class="notice notice-error"><p>The Newsletter dedicated page is not published. <a href="', site_url('/wp-admin/post.php') . '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
 
424
  } else {
425
  $content = get_post_field('post_content', $this->options['page']);
426
  // With and without attributes
427
  if (strpos($content, '[newsletter]') === false && strpos($content, '[newsletter ') === false) {
428
+ echo '<div class="notice notice-error"><p>The Newsletter dedicated page does not contain the [newsletter] shortcode. <a href="', site_url('/wp-admin/post.php'), '?post=', $this->options['page'], '&action=edit"><strong>Edit the page</strong></a> or <a href="admin.php?page=newsletter_main_main"><strong>review the main settings</strong></a>.</p></div>';
429
  }
430
  }
431
  }
432
+
433
+ $x = wp_next_scheduled('newsletter');
434
+ if ($x === false) {
435
+ echo '<div class="notice notice-error"><p>The Newsletter delivery engine is off (it should never be off). Deactivate and reactivate the Newsletter plugin.</p></div>';
436
+ } else if (time() - $x > 900) {
437
+ echo '<div class="notice notice-error"><p>The WP scheduler doesn\'t seem to be running correctly for Newsletter. <a href="https://www.thenewsletterplugin.com/documentation/newsletter-delivery-engine#cron" target="_blank"><strong>Read this page to solve the problem</strong></a>.</p></div>';
438
+ } else {
439
+ $cron_data = get_option('newsletter_diagnostic_cron_data');
440
+ if ($cron_data && $cron_data['mean'] > 500) {
441
+ echo '<div class="notice notice-error"><p>The WP scheduler doesn\'t seem to be triggered enough often for Newsletter. <a href="https://www.thenewsletterplugin.com/documentation/newsletter-delivery-engine#cron" target="_blank"><strong>Read this page to solve the problem</strong></a>.</p></div>';
442
+ }
443
+ }
444
  }
445
 
446
  function hook_wp_enqueue_scripts() {
readme.txt CHANGED
@@ -2,7 +2,7 @@
2
  Tags: newsletter,email,subscription,mass mail,list build,email marketing,direct mailing,automation,automated
3
  Requires at least: 3.4.0
4
  Tested up to: 4.9.6
5
- Stable tag: 5.4.9
6
  Contributors: satollo,webagile,michael-travan
7
 
8
  Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
@@ -102,6 +102,12 @@ Thank you, The Newsletter Team
102
 
103
  == Changelog ==
104
 
 
 
 
 
 
 
105
  = 5.4.9 =
106
 
107
  * Lists management in APIs
2
  Tags: newsletter,email,subscription,mass mail,list build,email marketing,direct mailing,automation,automated
3
  Requires at least: 3.4.0
4
  Tested up to: 4.9.6
5
+ Stable tag: 5.5.0
6
  Contributors: satollo,webagile,michael-travan
7
 
8
  Add a real newsletter system to your blog. For free. With unlimited newsletters and subscribers.
102
 
103
  == Changelog ==
104
 
105
+ = 5.5.0 =
106
+
107
+ * Added IP storage control
108
+ * Fixed a warning and a debug notice
109
+ * Aggregated warnings on admin side
110
+
111
  = 5.4.9 =
112
 
113
  * Lists management in APIs
statistics/statistics.php CHANGED
@@ -92,6 +92,7 @@ class NewsletterStatistics extends NewsletterModule {
92
  $is_action = strpos($url, '?na=');
93
 
94
  $ip = $this->get_remote_ip();
 
95
 
96
  if (!$is_action) {
97
  $this->add_click($url, $user_id, $email_id, $ip);
@@ -137,6 +138,7 @@ class NewsletterStatistics extends NewsletterModule {
137
  }
138
 
139
  $ip = $this->get_remote_ip();
 
140
 
141
  $this->add_click('', $user_id, $email_id, $ip);
142
  $this->update_open_value(self::SENT_READ, $user_id, $email_id, $ip);
@@ -312,6 +314,9 @@ class NewsletterStatistics extends NewsletterModule {
312
  if (is_null($ip)) {
313
  $ip = $this->get_remote_ip();
314
  }
 
 
 
315
  $this->insert(NEWSLETTER_STATS_TABLE, array(
316
  'email_id' => $email_id,
317
  'user_id' => $user_id,
@@ -326,6 +331,7 @@ class NewsletterStatistics extends NewsletterModule {
326
  if (is_null($ip)) {
327
  $ip = $this->get_remote_ip();
328
  }
 
329
  $this->query($wpdb->prepare("update " . NEWSLETTER_SENT_TABLE . " set open=%d, ip=%s where email_id=%d and user_id=%d and open<%d limit 1", $value, $ip, $email_id, $user_id, $value));
330
  }
331
 
92
  $is_action = strpos($url, '?na=');
93
 
94
  $ip = $this->get_remote_ip();
95
+ $ip = $this->process_ip($ip);
96
 
97
  if (!$is_action) {
98
  $this->add_click($url, $user_id, $email_id, $ip);
138
  }
139
 
140
  $ip = $this->get_remote_ip();
141
+ $ip = $this->process_ip($ip);
142
 
143
  $this->add_click('', $user_id, $email_id, $ip);
144
  $this->update_open_value(self::SENT_READ, $user_id, $email_id, $ip);
314
  if (is_null($ip)) {
315
  $ip = $this->get_remote_ip();
316
  }
317
+
318
+ $ip = $this->process_ip($ip);
319
+
320
  $this->insert(NEWSLETTER_STATS_TABLE, array(
321
  'email_id' => $email_id,
322
  'user_id' => $user_id,
331
  if (is_null($ip)) {
332
  $ip = $this->get_remote_ip();
333
  }
334
+ $ip = $this->process_ip($ip);
335
  $this->query($wpdb->prepare("update " . NEWSLETTER_SENT_TABLE . " set open=%d, ip=%s where email_id=%d and user_id=%d and open<%d limit 1", $value, $ip, $email_id, $user_id, $value));
336
  }
337
 
subscription/subscription.php CHANGED
@@ -35,7 +35,7 @@ class NewsletterSubscription extends NewsletterModule {
35
 
36
  function __construct() {
37
 
38
- parent::__construct('subscription', '2.1.6', null, array('lists', 'template', 'profile'));
39
  $this->options_profile = $this->get_options('profile');
40
  $this->options_lists = $this->get_options('lists');
41
 
@@ -492,6 +492,7 @@ class NewsletterSubscription extends NewsletterModule {
492
  $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_user_logs` (
493
  `id` int(11) NOT NULL AUTO_INCREMENT,
494
  `user_id` int(11) NOT NULL DEFAULT 0,
 
495
  `source` varchar(50) NOT NULL DEFAULT '',
496
  `data` longtext,
497
  `created` int(11) NOT NULL DEFAULT 0,
@@ -574,16 +575,20 @@ class NewsletterSubscription extends NewsletterModule {
574
 
575
  function set_updated($user, $time = 0, $ip = '') {
576
  global $wpdb;
577
- if (!$time)
578
  $time = time();
 
579
 
580
- if (!$ip)
581
  $ip = $this->get_remote_ip();
 
 
582
 
583
- if (is_object($user))
584
  $id = $user->id;
585
- else if (is_array($user))
586
  $id = $user['id'];
 
587
 
588
  $id = (int) $id;
589
 
@@ -698,7 +703,9 @@ class NewsletterSubscription extends NewsletterModule {
698
 
699
 
700
  $user['token'] = $this->get_token();
701
- $user['ip'] = $this->get_remote_ip();
 
 
702
  $user['status'] = $opt_in == self::OPTIN_SINGLE ? Newsletter::STATUS_CONFIRMED : Newsletter::STATUS_NOT_CONFIRMED;
703
 
704
  $user['updated'] = time();
35
 
36
  function __construct() {
37
 
38
+ parent::__construct('subscription', '2.1.7', null, array('lists', 'template', 'profile'));
39
  $this->options_profile = $this->get_options('profile');
40
  $this->options_lists = $this->get_options('lists');
41
 
492
  $sql = "CREATE TABLE `" . $wpdb->prefix . "newsletter_user_logs` (
493
  `id` int(11) NOT NULL AUTO_INCREMENT,
494
  `user_id` int(11) NOT NULL DEFAULT 0,
495
+ `ip` varchar(50) NOT NULL DEFAULT '',
496
  `source` varchar(50) NOT NULL DEFAULT '',
497
  `data` longtext,
498
  `created` int(11) NOT NULL DEFAULT 0,
575
 
576
  function set_updated($user, $time = 0, $ip = '') {
577
  global $wpdb;
578
+ if (!$time) {
579
  $time = time();
580
+ }
581
 
582
+ if (!$ip) {
583
  $ip = $this->get_remote_ip();
584
+ }
585
+ $ip = $this->process_ip($ip);
586
 
587
+ if (is_object($user)) {
588
  $id = $user->id;
589
+ } else if (is_array($user)) {
590
  $id = $user['id'];
591
+ }
592
 
593
  $id = (int) $id;
594
 
703
 
704
 
705
  $user['token'] = $this->get_token();
706
+ $ip = $this->get_remote_ip();
707
+ $ip = $this->process_ip($ip);
708
+ $user['ip'] = $ip;
709
  $user['status'] = $opt_in == self::OPTIN_SINGLE ? Newsletter::STATUS_CONFIRMED : Newsletter::STATUS_NOT_CONFIRMED;
710
 
711
  $user['updated'] = time();
tnp-header.php CHANGED
@@ -190,7 +190,6 @@ $warning |= empty($status_options['mail']);
190
  <?php } ?>
191
 
192
  <div id="tnp-notification">
193
- <?php Newsletter::instance()->warnings(); ?>
194
  <?php
195
  if (isset($controls)) {
196
  $controls->show();
190
  <?php } ?>
191
 
192
  <div id="tnp-notification">
 
193
  <?php
194
  if (isset($controls)) {
195
  $controls->show();
users/edit.php CHANGED
@@ -278,6 +278,7 @@ function percentValue($value, $total) {
278
  <tr>
279
  <th>Date</th>
280
  <th>Source</th>
 
281
  <th>Lists</th>
282
  </tr>
283
 
@@ -290,6 +291,7 @@ function percentValue($value, $total) {
290
  <tr>
291
  <td><?php echo $controls->print_date($log->created)?></td>
292
  <td><?php echo esc_html($log->source)?></td>
 
293
  <td>
294
  <?php
295
  if (is_array($data)) {
278
  <tr>
279
  <th>Date</th>
280
  <th>Source</th>
281
+ <th>IP</th>
282
  <th>Lists</th>
283
  </tr>
284
 
291
  <tr>
292
  <td><?php echo $controls->print_date($log->created)?></td>
293
  <td><?php echo esc_html($log->source)?></td>
294
+ <td><?php echo esc_html($log->ip)?></td>
295
  <td>
296
  <?php
297
  if (is_array($data)) {