Email Subscribers & Newsletters - Version 4.0.18

Version Description

(07.05.2019) = * New : Added a feature to duplicate any template * New : Added support to re run database migration from ES 3.5.18 * Fix : Display "0" above form * Fix : Migration issue

Download this release

Release Info

Developer Icegram
Plugin Icon 128x128 Email Subscribers & Newsletters
Version 4.0.18
Comparing to
See all releases

Code changes from version 4.0.17 to 4.0.18

admin/class-email-subscribers-admin.php CHANGED
@@ -65,8 +65,8 @@ class Email_Subscribers_Admin {
65
  add_action( 'admin_menu', array( $this, 'email_subscribers_admin_menu' ) );
66
  add_action( 'wp_ajax_es_klawoo_subscribe', array( $this, 'klawoo_subscribe' ) );
67
  add_action('admin_footer', array( $this, 'remove_submenu') );
 
68
 
69
- // self::admin_show();
70
  }
71
 
72
  /**
@@ -331,9 +331,6 @@ class Email_Subscribers_Admin {
331
  }
332
 
333
  public function es_dashboard_callback() {
334
- // if ( Email_Subscribers::get_request( 'dismiss_admin_notice' ) == 1 ) {
335
- // update_option( 'es_star_review', 1 );
336
- // }
337
  $es_plugin_data = get_plugin_data( plugin_dir_path( __DIR__ ) . 'email-subscribers.php' );
338
  $es_current_version = $es_plugin_data['Version'];
339
  $admin_email = get_option( 'admin_email' );
@@ -349,29 +346,17 @@ class Email_Subscribers_Admin {
349
  // }
350
  }
351
 
352
- public static function admin_show() {
353
- add_action( 'load-edit.php', function () {
354
- $screen = get_current_screen();
355
-
356
- if ( $screen->post_type == 'es_template' ) {
357
- add_action( 'all_admin_notices', array( 'Email_Subscribers_Admin', 'es_feedback' ) );
358
- }
359
- } );
360
-
361
- add_action( 'load-post.php', function () {
362
- $screen = get_current_screen();
363
-
364
- if ( $screen->post_type == 'es_template' ) {
365
- add_action( 'all_admin_notices', array( 'Email_Subscribers_Admin', 'es_feedback' ) );
366
- }
367
- } );
368
 
369
- add_action( 'load-post-new.php', function () {
370
- $screen = get_current_screen();
371
- if ( $screen->post_type == 'es_template' ) {
372
- add_action( 'all_admin_notices', array( 'Email_Subscribers_Admin', 'es_feedback' ) );
373
- }
374
- } );
 
 
 
 
375
  }
376
 
377
  }
65
  add_action( 'admin_menu', array( $this, 'email_subscribers_admin_menu' ) );
66
  add_action( 'wp_ajax_es_klawoo_subscribe', array( $this, 'klawoo_subscribe' ) );
67
  add_action('admin_footer', array( $this, 'remove_submenu') );
68
+ add_action( 'edit_form_advanced', array( $this, 'add_spam_score_utm_link' ) );
69
 
 
70
  }
71
 
72
  /**
331
  }
332
 
333
  public function es_dashboard_callback() {
 
 
 
334
  $es_plugin_data = get_plugin_data( plugin_dir_path( __DIR__ ) . 'email-subscribers.php' );
335
  $es_current_version = $es_plugin_data['Version'];
336
  $admin_email = get_option( 'admin_email' );
346
  // }
347
  }
348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
+ public static function add_spam_score_utm_link() {
351
+ global $post, $pagenow;
352
+ if ( $post->post_type !== 'es_template' ) {
353
+ return;
354
+ }
355
+ ?>
356
+ <script>
357
+ jQuery('#submitdiv').after('<div class="es_upsale"><a style="text-decoration:none;" target="_blank" href="https://www.icegram.com/documentation/how-ready-made-template-in-in-email-subscribers-look/?utm_source=in_app&utm_medium=es_template&utm_campaign=es_upsale"><img title="Get readymade templates" style="width:100%;border:0.3em #d46307 solid" src="<?php echo EMAIL_SUBSCRIBERS_URL?>/admin/images/starter-tmpl.png"/><p style="background: #d46307; color: #FFF; padding: 4px; width: 100%; text-align:center">Get readymade beautiful email templates</p></a></div>');
358
+ </script>
359
+ <?php
360
  }
361
 
362
  }
admin/css/email-subscribers-admin.css CHANGED
@@ -232,13 +232,13 @@ div#post-body-content .meta-box-sortables .row-blog p.submit input#submit {
232
  width: 100%;
233
  }
234
 
235
- .email-subscribers_page_es_newsletters table.form-table td select {
236
- height: 40px;
 
 
237
  width: 35%;
238
- line-height: 40px;
239
  }
240
 
241
-
242
  /* Customize Admin Settings */
243
 
244
  .ig-settings-form .ui-state-active, .ig-settings-form .ui-widget-content .ui-state-active, .ig-settings-form .ui-widget-header .ui-state-active, .ig-settings-form a.ui-button:active, .ig-settings-form .ui-button:active, .ig-settings-form .ui-button.ui-state-active:hover {
@@ -894,4 +894,15 @@ h5.es-badge {
894
  height: 20px;
895
  margin-left: 0.3em;
896
  vertical-align: middle;
 
 
 
 
 
 
 
 
 
 
 
897
  }
232
  width: 100%;
233
  }
234
 
235
+ .email-subscribers_page_es_newsletters table.form-table td select,
236
+ .email-subscribers_page_es_notifications table.form-table td select,
237
+ .email-subscribers_page_es_newsletters table.form-table input[type="text"] ,
238
+ .email-subscribers_page_es_notifications table.form-table input[type="text"]{
239
  width: 35%;
 
240
  }
241
 
 
242
  /* Customize Admin Settings */
243
 
244
  .ig-settings-form .ui-state-active, .ig-settings-form .ui-widget-content .ui-state-active, .ig-settings-form .ui-widget-header .ui-state-active, .ig-settings-form a.ui-button:active, .ig-settings-form .ui-button:active, .ig-settings-form .ui-button.ui-state-active:hover {
894
  height: 20px;
895
  margin-left: 0.3em;
896
  vertical-align: middle;
897
+ }
898
+
899
+ .es-upsale-image img{
900
+ margin-left: -16em;
901
+ }
902
+ .es-smtp-image img{
903
+ margin-left: -1em;
904
+ margin-top: 0.8em;
905
+ }
906
+ .es-smtp-label{
907
+ margin-left: -0.6em;
908
  }
admin/images/es-captcha-2.png ADDED
Binary file
admin/images/es-smtp-label.png ADDED
Binary file
admin/images/es-smtp.png ADDED
Binary file
admin/images/starter-tmpl.png ADDED
Binary file
admin/js/mycustom.js CHANGED
@@ -1,3 +1,8 @@
1
- jQuery(document).ready(function ($) {
2
- $('input#subscriber-search-input').select2();
 
 
 
 
 
3
  });
1
+ jQuery(document).ready(function () {
2
+ jQuery('li.es-card').first().addClass('active');
3
+ jQuery('.control_next').on( 'click', function(){
4
+ console.log(999);
5
+ jQuery('.es-card').next().addClass('active');
6
+
7
+ });
8
  });
admin/partials/help.php CHANGED
@@ -29,6 +29,24 @@ if ( ! defined( 'ABSPATH' ) ) {
29
  </div>
30
  </div>
31
  <div class="right-blog">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  <div class="subscribe-form">
33
  <h4><?php echo __( 'Additional form settings', 'email-subscribers' ); ?></h4>
34
  <ul>
29
  </div>
30
  </div>
31
  <div class="right-blog">
32
+
33
+ <?php if($enable_manual_update) { ?>
34
+
35
+ <div class="database-migration">
36
+ <h3><?php echo __('Database Migration', 'email-subscribers'); ?></h3>
37
+
38
+ <p><?php echo __( 'If you found duplicate campaigns, lists, forms, reports after upgrading from Email Subscribers 3.5.x to 4.x and want to run the database migration again to fix this, please click the below <b>Run the updater</b> button.', 'email-subscribers'); ?></p>
39
+
40
+ <p><?php echo __('Once you click on <b>Run the updater</b> button, it will run the migration process from 3.5.x once again. So, if you have created new campaigns, forms or lists after migration to 4.x earlier, you will lose those data. So, make sure you have a backup with you.', 'email-subscribers'); ?></p>
41
+
42
+ <p class="submit">
43
+ <a href="<?php echo esc_url( $update_url ); ?>" class="es-update-now button-primary"><?php echo __('Run the updater', 'email-subscribers'); ?></a>
44
+ </p>
45
+
46
+ </div>
47
+
48
+ <?php } ?>
49
+
50
  <div class="subscribe-form">
51
  <h4><?php echo __( 'Additional form settings', 'email-subscribers' ); ?></h4>
52
  <ul>
admin/partials/pricing.php CHANGED
@@ -46,7 +46,7 @@ if ( ! defined( 'ABSPATH' ) ) {
46
  </style>
47
  <div class="es-starter-gopro">
48
  <img src="<?php echo EMAIL_SUBSCRIBERS_URL.'/admin/images/pricing.png' ?>"/><br/>
49
- <a class="button large green-light" href="https://www.icegram.com/email-subscribers-pricing/?utm_source=in_app&utm_medium=go_pro&utm_campaign=starter_launch" target="_blank"> <?php _e('Get Started @ $5/month', 'email-subscribers') ?></a>
50
  <div class="mid-or">OR</div>
51
- <a class="button large green" href="https://www.icegram.com/email-subscribers-pricing/?utm_source=in_app&utm_medium=go_pro&utm_campaign=starter_launch" target="_blank"> <?php _e('Get Started @ $29/year', 'email-subscribers') ?></a>
52
  </div>
46
  </style>
47
  <div class="es-starter-gopro">
48
  <img src="<?php echo EMAIL_SUBSCRIBERS_URL.'/admin/images/pricing.png' ?>"/><br/>
49
+ <a class="button large green-light" href="https://www.icegram.com/email-subscribers-pricing/?utm_source=in_app&utm_medium=go_pro_monthly&utm_campaign=starter_launch" target="_blank"> <?php _e('Get Started @ $5/month', 'email-subscribers') ?></a>
50
  <div class="mid-or">OR</div>
51
+ <a class="button large green" href="https://www.icegram.com/email-subscribers-pricing/?utm_source=in_app&utm_medium=go_pro_yearly&utm_campaign=starter_launch" target="_blank"> <?php _e('Get Started @ $29/year', 'email-subscribers') ?></a>
52
  </div>
email-subscribers.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Email Subscribers & Newsletters
4
  * Plugin URI: https://www.icegram.com/
5
  * Description: Add subscription forms on website, send HTML newsletters & automatically notify subscribers about new blog posts once it is published.
6
- * Version: 4.0.17
7
  * Author: Icegram
8
  * Author URI: https://www.icegram.com/
9
  * Requires at least: 3.9
@@ -24,7 +24,7 @@ if ( ! defined( 'WPINC' ) ) {
24
  * Define constants
25
  */
26
  define( 'ES_PLUGIN_DIR', dirname( __FILE__ ) );
27
- define( 'ES_PLUGIN_VERSION', '4.0.17' );
28
  define( 'ES_PLUGIN_BASE_NAME', plugin_basename( __FILE__ ) );
29
 
30
  if ( ! defined( 'ES_PLUGIN_FILE' ) ) {
3
  * Plugin Name: Email Subscribers & Newsletters
4
  * Plugin URI: https://www.icegram.com/
5
  * Description: Add subscription forms on website, send HTML newsletters & automatically notify subscribers about new blog posts once it is published.
6
+ * Version: 4.0.18
7
  * Author: Icegram
8
  * Author URI: https://www.icegram.com/
9
  * Requires at least: 3.9
24
  * Define constants
25
  */
26
  define( 'ES_PLUGIN_DIR', dirname( __FILE__ ) );
27
+ define( 'ES_PLUGIN_VERSION', '4.0.18' );
28
  define( 'ES_PLUGIN_BASE_NAME', plugin_basename( __FILE__ ) );
29
 
30
  if ( ! defined( 'ES_PLUGIN_FILE' ) ) {
includes/admin/class-es-admin-settings.php CHANGED
@@ -26,6 +26,7 @@ class ES_Admin_Settings {
26
  public function __construct() {
27
  //add_filter( 'set-screen-option', [ __CLASS__, 'set_screen' ], 10, 3 );
28
  // add_action( 'admin_menu', [ $this, 'plugin_menu' ] );
 
29
  }
30
 
31
  public static function set_screen( $status, $option, $value ) {
@@ -486,6 +487,7 @@ class ES_Admin_Settings {
486
  'info' => __( 'Seeing spam signups from particular domains? Enter domains names (one per line) that you want to block here.', 'email-subscribers' ),
487
  'default' => 'mail.ru'
488
  ),
 
489
  )
490
 
491
 
@@ -632,4 +634,33 @@ class ES_Admin_Settings {
632
 
633
  }
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
  }
26
  public function __construct() {
27
  //add_filter( 'set-screen-option', [ __CLASS__, 'set_screen' ], 10, 3 );
28
  // add_action( 'admin_menu', [ $this, 'plugin_menu' ] );
29
+ add_filter( 'ig_es_registered_settings', array( &$this, 'es_add_upsale' ), 10, 2 );
30
  }
31
 
32
  public static function set_screen( $status, $option, $value ) {
487
  'info' => __( 'Seeing spam signups from particular domains? Enter domains names (one per line) that you want to block here.', 'email-subscribers' ),
488
  'default' => 'mail.ru'
489
  ),
490
+
491
  )
492
 
493
 
634
 
635
  }
636
 
637
+ function es_add_upsale( $fields ) {
638
+ $active_plugins = (array) get_option( 'active_plugins', array() );
639
+ if ( is_multisite() ) {
640
+ $active_plugins = array_merge( $active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
641
+ }
642
+
643
+ if ( ! ( in_array( 'email-subscribers-premium/email-subscribers-premium.php', $active_plugins ) || array_key_exists( 'email-subscribers-premium/email-subscribers-premium.php', $active_plugins ) ) ) {
644
+ //add upsale
645
+ $field_security['es_upsale_security'] = array(
646
+ 'id' => 'ig_es_blocked_domains',
647
+ 'type' => 'html',
648
+ 'name' => '',
649
+ 'html' => '<div class="es-upsale-image" style=""><a target="_blank" href="https://www.icegram.com/email-subscribers-starter/?utm_source=in_app&utm_medium=es_security_settings&utm_campaign=es_upsale"><image src="'.EMAIL_SUBSCRIBERS_URL. '/admin/images/es-captcha-2.png'.'"/></a></div>'
650
+ );
651
+ $fields['security_settings'] = array_merge( $fields['security_settings'], $field_security );
652
+
653
+ //add upsale
654
+ $field_smtp['es_upsale_smtp'] = array(
655
+ 'id' => 'ig_es_blocked_domains',
656
+ 'type' => 'html',
657
+ 'name' => '<div class="es-smtp-label" style=""><a target="_blank" href="https://www.icegram.com/email-subscribers-starter/?utm_source=in_app&utm_medium=es_security_settings&utm_campaign=es_upsale"><img src="'.EMAIL_SUBSCRIBERS_URL. '/admin/images/es-smtp-label.png'.'"/></a></div>',
658
+ 'html' => '<div class="es-upsale-image es-smtp-image" style=""><a target="_blank" href="https://www.icegram.com/email-subscribers-starter/?utm_source=in_app&utm_medium=es_security_settings&utm_campaign=es_upsale"><img src="'.EMAIL_SUBSCRIBERS_URL. '/admin/images/es-smtp.png'.'"/></a></div>'
659
+ );
660
+ $fields['email_sending'] = array_merge( $fields['email_sending'], $field_smtp );
661
+
662
+ }
663
+ return $fields;
664
+ }
665
+
666
  }
includes/admin/class-es-forms-table.php CHANGED
@@ -51,6 +51,7 @@ class ES_Forms_Table extends WP_List_Table {
51
  $this->display(); ?>
52
  </form>
53
  </div>
 
54
  </div>
55
  </div>
56
  <br class="clear">
@@ -333,8 +334,10 @@ class ES_Forms_Table extends WP_List_Table {
333
  $message = __( sprintf( 'List(s) not found. Please create a first list from <a href="%s">here</a>', $lists_page_url ), 'email-subscribers' );
334
  $status = 'error';
335
  $this->show_message( $message, $status );
336
- } ?>
337
-
 
 
338
  </form>
339
  </div>
340
  </div>
51
  $this->display(); ?>
52
  </form>
53
  </div>
54
+
55
  </div>
56
  </div>
57
  <br class="clear">
334
  $message = __( sprintf( 'List(s) not found. Please create a first list from <a href="%s">here</a>', $lists_page_url ), 'email-subscribers' );
335
  $status = 'error';
336
  $this->show_message( $message, $status );
337
+ }
338
+ $url = 'https://www.icegram.com/email-subscribers-starter/?utm_source=in_app&utm_medium=es_form_captcha&utm_campaign=es_upsale';
339
+ ?>
340
+ <div style=" background-image: linear-gradient(-100deg, rgba(250, 247, 133, 0.4), rgba(250, 247, 133, 0.8) 95%, rgba(250, 247, 133, 0.2)); padding: 10px; width: 35%; border-radius: 1em 0 1em 0; "><?php echo sprintf( __('Secure you form and avoid spam signups with Email Subscribers Starter Plan <a target="_blank" style="font-weight: bold; cursor:pointer; text-decoration:none" href="%s">Get started</a>', 'email-subscribers'), $url)?></div>
341
  </form>
342
  </div>
343
  </div>
includes/admin/class-es-handle-subscription.php CHANGED
@@ -49,14 +49,6 @@ class ES_Handle_Subscription {
49
 
50
  if ( ( isset( $_POST['es'] ) ) && ( 'subscribe' === $_POST['es'] ) && ! empty( $_POST['esfpx_es-subscribe'] ) ) {
51
 
52
- // Restrict too many requests
53
- $timeout = ES_Subscription_Throttaling::throttle();
54
- if ( $timeout > 0 ) {
55
- $response['message'] = 'es_rate_limit_notice';
56
- $this->do_response( $response );
57
- exit;
58
- }
59
-
60
  $form_data = wp_unslash( $_POST );
61
  $validate_response = $this->validate_data( $form_data );
62
  if ( $validate_response['status'] === 'ERROR' ) {
@@ -347,6 +339,12 @@ class ES_Handle_Subscription {
347
  return $es_response;
348
  }
349
 
 
 
 
 
 
 
350
  $es_response['status'] = 'SUCCESS';
351
 
352
  return $es_response;
49
 
50
  if ( ( isset( $_POST['es'] ) ) && ( 'subscribe' === $_POST['es'] ) && ! empty( $_POST['esfpx_es-subscribe'] ) ) {
51
 
 
 
 
 
 
 
 
 
52
  $form_data = wp_unslash( $_POST );
53
  $validate_response = $this->validate_data( $form_data );
54
  if ( $validate_response['status'] === 'ERROR' ) {
339
  return $es_response;
340
  }
341
 
342
+ $timeout = ES_Subscription_Throttaling::throttle();
343
+ if ( $timeout > 0 ) {
344
+ $response['message'] = 'es_rate_limit_notice';
345
+ return $response;
346
+ }
347
+
348
  $es_response['status'] = 'SUCCESS';
349
 
350
  return $es_response;
includes/admin/class-es-info.php CHANGED
@@ -30,6 +30,18 @@ class ES_Info {
30
  }
31
 
32
  public function es_information_callback() {
 
 
 
 
 
 
 
 
 
 
 
 
33
  include_once( EMAIL_SUBSCRIBERS_DIR . '/admin/partials/help.php' );
34
  }
35
 
30
  }
31
 
32
  public function es_information_callback() {
33
+
34
+ $is_option_exists = get_option('current_sa_email_subscribers_db_version', false);
35
+ $enable_manual_update = false;
36
+ if($is_option_exists) {
37
+ $enable_manual_update = true;
38
+ }
39
+
40
+ $update_url = add_query_arg( 'do_update_ig_es', 'true', admin_url( 'admin.php?page=es_general_information' ) );
41
+ $update_url = add_query_arg( 'from_db_version', '3.5.18', $update_url );
42
+ $update_url = wp_nonce_url( $update_url, 'ig_es_db_update','ig_es_db_update_nonce');
43
+
44
+
45
  include_once( EMAIL_SUBSCRIBERS_DIR . '/admin/partials/help.php' );
46
  }
47
 
includes/admin/class-es-post-notifications.php CHANGED
@@ -36,7 +36,6 @@ class ES_Post_Notifications_Table {
36
 
37
  $list_id = Email_Subscribers::get_request( 'list_id' );
38
  $template_id = Email_Subscribers::get_request( 'template_id' );
39
- // $es_note_status = Email_Subscribers::get_request( 'es_note_status' );
40
  $cat = Email_Subscribers::get_request( 'es_note_cat' );
41
 
42
  if ( empty( $list_id ) ) {
@@ -62,8 +61,10 @@ class ES_Post_Notifications_Table {
62
 
63
  return;
64
  }
 
65
  $type = 'post_notification';
66
  $title = get_the_title( $template_id );
 
67
  $data = array(
68
  'categories' => ES_Common::convert_categories_array_to_string( $cat ),
69
  'list_ids' => $list_id,
@@ -182,8 +183,7 @@ class ES_Post_Notifications_Table {
182
  if(!empty($data['base_template_id'])) {
183
  $title = get_the_title( $data['base_template_id'] );
184
  }
185
-
186
- $data['name'] = $title;
187
 
188
  $this->save_list( $data, $id );
189
 
@@ -220,7 +220,6 @@ class ES_Post_Notifications_Table {
220
  $action = 'edit';
221
  $heading = __( 'Campigns > Edit Post Notification', 'email-subscribers' );
222
  }
223
-
224
  $cat = isset( $data['categories'] ) ? $data['categories'] : '';
225
  $list_id = isset( $data['list_ids'] ) ? $data['list_ids'] : '';
226
  $template_id = isset( $data['base_template_id'] ) ? $data['base_template_id'] : '';
36
 
37
  $list_id = Email_Subscribers::get_request( 'list_id' );
38
  $template_id = Email_Subscribers::get_request( 'template_id' );
 
39
  $cat = Email_Subscribers::get_request( 'es_note_cat' );
40
 
41
  if ( empty( $list_id ) ) {
61
 
62
  return;
63
  }
64
+
65
  $type = 'post_notification';
66
  $title = get_the_title( $template_id );
67
+
68
  $data = array(
69
  'categories' => ES_Common::convert_categories_array_to_string( $cat ),
70
  'list_ids' => $list_id,
183
  if(!empty($data['base_template_id'])) {
184
  $title = get_the_title( $data['base_template_id'] );
185
  }
186
+ $data['name'] = $title;
 
187
 
188
  $this->save_list( $data, $id );
189
 
220
  $action = 'edit';
221
  $heading = __( 'Campigns > Edit Post Notification', 'email-subscribers' );
222
  }
 
223
  $cat = isset( $data['categories'] ) ? $data['categories'] : '';
224
  $list_id = isset( $data['list_ids'] ) ? $data['list_ids'] : '';
225
  $template_id = isset( $data['base_template_id'] ) ? $data['base_template_id'] : '';
includes/admin/class-es-templates-table.php CHANGED
@@ -16,6 +16,9 @@ class ES_Templates_Table {
16
  add_filter( 'manage_edit-es_template_columns', array( $this, 'add_new_columns' ), 10, 1 );
17
  add_action( 'manage_posts_custom_column', array( $this, 'custom_columns' ) );
18
  add_action( 'admin_footer', array( $this, 'add_custom_button' ) );
 
 
 
19
  }
20
 
21
  public function es_template_meta_box_add() {
@@ -223,6 +226,60 @@ class ES_Templates_Table {
223
  return $column;
224
  }
225
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  public static function get_instance() {
227
  if ( ! isset( self::$instance ) ) {
228
  self::$instance = new self();
16
  add_filter( 'manage_edit-es_template_columns', array( $this, 'add_new_columns' ), 10, 1 );
17
  add_action( 'manage_posts_custom_column', array( $this, 'custom_columns' ) );
18
  add_action( 'admin_footer', array( $this, 'add_custom_button' ) );
19
+ //duplicate template
20
+ add_filter( 'post_row_actions', array(&$this , 'add_message_action'), 10, 2 );
21
+ add_action('admin_init', array(&$this ,'duplicate_message') ,10, 1);
22
  }
23
 
24
  public function es_template_meta_box_add() {
226
  return $column;
227
  }
228
 
229
+ function add_message_action( $actions, $post ){
230
+ if ($post->post_type != 'es_template') return $actions;
231
+ $actions['duplicate_template'] = '<a class="es-duplicate-template" href="post.php?template_id='.$post->ID.'&action=duplicate-template" >'.__('Duplicate' ,'email-subscribers').'</a>';
232
+ return $actions;
233
+ }
234
+
235
+ function duplicate_message(){
236
+ if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'duplicate-template' && !empty($_REQUEST['template_id'])){
237
+ //duplicate tempalte
238
+ $duplicate_template_id = $this->duplicate_in_db( $_REQUEST['template_id'] );
239
+ // $location = admin_url( 'post.php?post='.$duplicate_template_id.'&action=edit');
240
+ $location = admin_url( 'edit.php?post_type=es_template');
241
+ wp_safe_redirect($location);
242
+ exit;
243
+ }
244
+ }
245
+
246
+ function duplicate_in_db( $original_id){
247
+ // Get access to the database
248
+ global $wpdb;
249
+ // Get the post as an array
250
+ $duplicate = get_post( $original_id, 'ARRAY_A' );
251
+ // Modify some of the elements
252
+ $duplicate['post_title'] = $duplicate['post_title'].' '.__('Copy', 'icegram');
253
+ $duplicate['post_status'] = 'draft';
254
+ // Set the post date
255
+ $timestamp = current_time('timestamp', 0);
256
+
257
+ $duplicate['post_date'] = date('Y-m-d H:i:s', $timestamp);
258
+
259
+ // Remove some of the keys
260
+ unset( $duplicate['ID'] );
261
+ unset( $duplicate['guid'] );
262
+ unset( $duplicate['comment_count'] );
263
+
264
+ // Insert the post into the database
265
+ $duplicate_id = wp_insert_post( $duplicate );
266
+
267
+ // Duplicate all taxonomies/terms
268
+ $taxonomies = get_object_taxonomies( $duplicate['post_type'] );
269
+
270
+ foreach( $taxonomies as $taxonomy ) {
271
+ $terms = wp_get_post_terms( $original_id, $taxonomy, array('fields' => 'names') );
272
+ wp_set_object_terms( $duplicate_id, $terms, $taxonomy );
273
+ }
274
+
275
+ // Duplicate all custom fields
276
+ $custom_fields = get_post_custom( $original_id );
277
+ foreach ( $custom_fields as $key => $value ) {
278
+ add_post_meta( $duplicate_id, $key, maybe_unserialize($value[0]) );
279
+ }
280
+ return $duplicate_id;
281
+ }
282
+
283
  public static function get_instance() {
284
  if ( ! isset( self::$instance ) ) {
285
  self::$instance = new self();
includes/class-es-common.php CHANGED
@@ -482,6 +482,7 @@ Class ES_Common {
482
  $categories_str = '';
483
  if ( is_array( $categories ) && count( $categories ) > 0 ) {
484
  $categories_str = "##" . implode( '##', $categories ) . "##";
 
485
  }
486
 
487
  return $categories_str;
482
  $categories_str = '';
483
  if ( is_array( $categories ) && count( $categories ) > 0 ) {
484
  $categories_str = "##" . implode( '##', $categories ) . "##";
485
+ $categories_str = wp_specialchars_decode( $categories_str, ENT_QUOTES );
486
  }
487
 
488
  return $categories_str;
includes/class-es-install.php CHANGED
@@ -57,6 +57,7 @@ class ES_Install {
57
  * - Get all data from es_deliverreport and import into ig_sending_queue table
58
  * - Import all data from es_subscriber_ips to ig_contacts_ips
59
  */
 
60
  'ig_es_update_400_create_tables',
61
  'ig_es_update_400_import_options',
62
  'ig_es_update_400_migrate_lists',
@@ -92,7 +93,6 @@ class ES_Install {
92
  ),
93
 
94
  '4.0.10' => array(
95
- 'ig_es_update_4010_update_sending_status',
96
  'ig_es_update_4010_db_version'
97
  ),
98
 
@@ -147,6 +147,12 @@ class ES_Install {
147
  public static function install_actions() {
148
  if ( ! empty( $_GET['do_update_ig_es'] ) ) { // WPCS: input var ok.
149
  check_admin_referer( 'ig_es_db_update', 'ig_es_db_update_nonce' );
 
 
 
 
 
 
150
  self::update();
151
  ES_Admin_Notices::add_notice( 'update' );
152
  }
@@ -253,7 +259,7 @@ class ES_Install {
253
  return;
254
  }
255
 
256
- set_transient( 'ig_es_updating', 'yes', MINUTE_IN_SECONDS * 20 );
257
 
258
  $current_db_version = get_ig_es_db_version();
259
  $update_queued = false;
57
  * - Get all data from es_deliverreport and import into ig_sending_queue table
58
  * - Import all data from es_subscriber_ips to ig_contacts_ips
59
  */
60
+ 'ig_es_update_400_delete_tables',
61
  'ig_es_update_400_create_tables',
62
  'ig_es_update_400_import_options',
63
  'ig_es_update_400_migrate_lists',
93
  ),
94
 
95
  '4.0.10' => array(
 
96
  'ig_es_update_4010_db_version'
97
  ),
98
 
147
  public static function install_actions() {
148
  if ( ! empty( $_GET['do_update_ig_es'] ) ) { // WPCS: input var ok.
149
  check_admin_referer( 'ig_es_db_update', 'ig_es_db_update_nonce' );
150
+ $from_db_version = !empty($_GET['from_db_version']) ? $_GET['from_db_version'] : '';
151
+
152
+ if(!empty($from_db_version)) {
153
+ self::update_db_version( $from_db_version );
154
+ }
155
+
156
  self::update();
157
  ES_Admin_Notices::add_notice( 'update' );
158
  }
259
  return;
260
  }
261
 
262
+ set_transient( 'ig_es_updating', 'yes', MINUTE_IN_SECONDS * 30 );
263
 
264
  $current_db_version = get_ig_es_db_version();
265
  $update_queued = false;
includes/db/class-es-db-sending-queue.php CHANGED
@@ -240,55 +240,79 @@ class ES_DB_Sending_Queue {
240
 
241
  $total_bataches = ( $total > IG_DEFAULT_BATCH_SIZE ) ? ceil( $total / $batch_size ) : 1;
242
 
243
- for ( $i = 0; $i < $total_bataches; $i ++ ) {
244
- $batch_start = $i * $batch_size;
245
-
246
- $query = "SELECT * FROM " . EMAIL_SUBSCRIBERS_STATS_TABLE . " LIMIT {$batch_start}, {$batch_size}";
247
- $results = $wpdb->get_results( $query, ARRAY_A );
248
- $values = $data = $place_holders = array();
249
- foreach ( $results as $key => $result ) {
250
-
251
- $email = $result['es_deliver_emailmail'];
252
- $is_opened = ( $result['es_deliver_viewdate'] != '0000-00-00 00:00:00' ) ? 1 : 0;
253
-
254
- $contact_id = 0;
255
- $hash = '';
256
- if ( isset( $email_details[ $email ] ) ) {
257
- $contact_id = $email_details[ $email ]['id'];
258
- $hash = $email_details[ $email ]['hash'];
259
- }
260
 
261
- $data['mailing_queue_id'] = $mailing_queue_details[ $result['es_deliver_sentguid'] ]['id'];
262
- $data['mailing_queue_hash'] = $result['es_deliver_sentguid'];
263
- $data['contact_id'] = $contact_id;
264
- $data['contact_hash'] = $hash;
265
- $data['email'] = $email;
266
- $data['status'] = $result['es_deliver_sentstatus'];
267
- $data['opened'] = $is_opened;
268
- $data['sent_at'] = $mailing_queue_details[ $result['es_deliver_sentguid'] ]['start_at'];
269
- $data['opened_at'] = $result['es_deliver_viewdate'];
270
 
271
- $data = wp_parse_args( $data, self::get_column_defaults() );
 
272
 
273
- $formats = array();
274
- foreach ( $columns as $column => $format ) {
275
- $values[] = $data[ $column ];
276
- $formats[] = $format;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  }
278
 
279
- $place_holders[] = "( " . implode( ', ', $formats ) . " )";
280
- }
 
281
 
 
 
 
282
 
283
- ES_DB::do_insert( IG_SENDING_QUEUE_TABLE, $fields, $place_holders, $values );
284
  }
285
  }
286
  }
287
 
288
- public static function delete_sending_queue_by_mailing_id( $mailing_queue_ids ){
289
  global $wpdb;
290
 
291
- $mailing_queue_ids = implode( ',', array_map( 'absint', $mailing_queue_ids ) );
292
 
293
  $query = "DELETE FROM " . IG_SENDING_QUEUE_TABLE . " WHERE mailing_queue_id IN ($mailing_queue_ids)";
294
 
240
 
241
  $total_bataches = ( $total > IG_DEFAULT_BATCH_SIZE ) ? ceil( $total / $batch_size ) : 1;
242
 
243
+ $last_sending_queue_batch_run = get_transient( 'last_sending_queue_batch_run' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ if ( false === $last_sending_queue_batch_run ) {
246
+ $batch_start_from = 0;
247
+ } else {
248
+ $batch_start_from = $last_sending_queue_batch_run + 1;
249
+ }
 
 
 
 
250
 
251
+ $logger = get_ig_logger();
252
+ $logger->info( 'Sending Queue Start From: ' . $batch_start_from, array( 'source' => 'es_update' ) );
253
 
254
+
255
+ for ( $i = $batch_start_from; $i < $total_bataches; $i ++ ) {
256
+
257
+ if ( false === get_transient( 'running_migration_for_' . $i ) ) {
258
+
259
+ set_transient( 'running_migration_for_' . $i, true, 300 );
260
+ $batch_start = $i * $batch_size;
261
+
262
+ $query = "SELECT * FROM " . EMAIL_SUBSCRIBERS_STATS_TABLE . " LIMIT {$batch_start}, {$batch_size}";
263
+ $results = $wpdb->get_results( $query, ARRAY_A );
264
+ $values = $data = $place_holders = array();
265
+ foreach ( $results as $key => $result ) {
266
+
267
+ $email = $result['es_deliver_emailmail'];
268
+ $is_opened = ( $result['es_deliver_viewdate'] != '0000-00-00 00:00:00' ) ? 1 : 0;
269
+
270
+ $contact_id = 0;
271
+ $hash = '';
272
+ if ( isset( $email_details[ $email ] ) ) {
273
+ $contact_id = $email_details[ $email ]['id'];
274
+ $hash = $email_details[ $email ]['hash'];
275
+ }
276
+
277
+ $mailing_queue_id = ! empty( $mailing_queue_details[ $result['es_deliver_sentguid'] ] ) ? $mailing_queue_details[ $result['es_deliver_sentguid'] ]['id'] : 0;
278
+ $start_at = ! empty( $mailing_queue_details[ $result['es_deliver_sentguid'] ] ) ? $mailing_queue_details[ $result['es_deliver_sentguid'] ]['start_at'] : '0000-00-00 00:00:00';
279
+ $data['mailing_queue_id'] = $mailing_queue_id;
280
+ $data['mailing_queue_hash'] = $result['es_deliver_sentguid'];
281
+ $data['contact_id'] = $contact_id;
282
+ $data['contact_hash'] = $hash;
283
+ $data['email'] = $email;
284
+ $data['status'] = $result['es_deliver_sentstatus'];
285
+ $data['opened'] = $is_opened;
286
+ $data['sent_at'] = $start_at;
287
+ $data['opened_at'] = $result['es_deliver_viewdate'];
288
+
289
+ $data = wp_parse_args( $data, self::get_column_defaults() );
290
+
291
+ $formats = array();
292
+ foreach ( $columns as $column => $format ) {
293
+ $values[] = $data[ $column ];
294
+ $formats[] = $format;
295
+ }
296
+
297
+ $place_holders[] = "( " . implode( ', ', $formats ) . " )";
298
  }
299
 
300
+ $logger->info( '------------------[Running.....]: ' . $i, array( 'source' => 'es_update' ) );
301
+ ES_DB::do_insert( IG_SENDING_QUEUE_TABLE, $fields, $place_holders, $values );
302
+ delete_transient( 'running_migration_for_' . $i );
303
 
304
+ $logger->info( '------------------[last_sending_queue_batch_run]: ' . $i, array( 'source' => 'es_update' ) );
305
+ set_transient( 'last_sending_queue_batch_run', $i, MINUTE_IN_SECONDS * 100 );
306
+ }
307
 
 
308
  }
309
  }
310
  }
311
 
312
+ public static function delete_sending_queue_by_mailing_id( $mailing_queue_ids ) {
313
  global $wpdb;
314
 
315
+ $mailing_queue_ids = implode( ',', array_map( 'absint', $mailing_queue_ids ) );
316
 
317
  $query = "DELETE FROM " . IG_SENDING_QUEUE_TABLE . " WHERE mailing_queue_id IN ($mailing_queue_ids)";
318
 
includes/libraries/wp-background-process.php CHANGED
@@ -95,6 +95,8 @@ abstract class WP_Background_Process extends WP_Async_Request {
95
  * @return $this
96
  */
97
  public function save() {
 
 
98
  $key = $this->generate_key();
99
 
100
  if ( ! empty( $this->data ) ) {
@@ -113,6 +115,8 @@ abstract class WP_Background_Process extends WP_Async_Request {
113
  * @return $this
114
  */
115
  public function update( $key, $data ) {
 
 
116
  if ( ! empty( $data ) ) {
117
  update_site_option( $key, $data );
118
  }
95
  * @return $this
96
  */
97
  public function save() {
98
+ $logger = get_ig_logger();
99
+ $logger->trace('Saving Another Batch');
100
  $key = $this->generate_key();
101
 
102
  if ( ! empty( $this->data ) ) {
115
  * @return $this
116
  */
117
  public function update( $key, $data ) {
118
+ $logger = get_ig_logger();
119
+ $logger->trace('Update Another Batch');
120
  if ( ! empty( $data ) ) {
121
  update_site_option( $key, $data );
122
  }
includes/notices/views/html-notice-updating.php CHANGED
@@ -16,9 +16,11 @@ $force_update_url = wp_nonce_url(
16
  ?>
17
  <div id="message" class="updated es-message es-connect">
18
  <p>
19
- <strong><?php esc_html_e( 'Email Subscribers data update', 'email-subscribers' ); ?></strong> &#8211; <?php esc_html_e( 'Your database is being updated in the background.', 'email-subscribers' ); ?>
 
20
  <a href="<?php echo esc_url( $force_update_url ); ?>">
21
  <?php esc_html_e( 'Taking a while? Click here to run it now.', 'email-subscribers' ); ?>
22
  </a>
 
23
  </p>
24
  </div>
16
  ?>
17
  <div id="message" class="updated es-message es-connect">
18
  <p>
19
+ <strong><?php esc_html_e( 'Email Subscribers data update', 'email-subscribers' ); ?></strong> &#8211; <?php esc_html_e( 'Your database is being updated in the background. Please be patient.', 'email-subscribers' ); ?>
20
+ <!--
21
  <a href="<?php echo esc_url( $force_update_url ); ?>">
22
  <?php esc_html_e( 'Taking a while? Click here to run it now.', 'email-subscribers' ); ?>
23
  </a>
24
+ -->
25
  </p>
26
  </div>
includes/upgrade/class-es-background-process.php CHANGED
@@ -93,16 +93,28 @@ abstract class ES_Background_Process extends WP_Background_Process {
93
  */
94
  protected function handle() {
95
  $this->lock_process();
 
96
 
97
  do {
98
  $batch = $this->get_batch();
99
 
 
100
  foreach ( $batch->data as $key => $value ) {
101
- $task = $this->task( $value );
 
 
 
 
 
 
 
 
102
 
103
  if ( false !== $task ) {
104
  $batch->data[ $key ] = $task;
105
  } else {
 
 
106
  unset( $batch->data[ $key ] );
107
  }
108
 
@@ -143,7 +155,7 @@ abstract class ES_Background_Process extends WP_Background_Process {
143
  $memory_limit = '128M';
144
  }
145
 
146
- if ( ! $memory_limit || -1 === intval( $memory_limit ) ) {
147
  // Unlimited, set to 32GB.
148
  $memory_limit = '32G';
149
  }
@@ -155,6 +167,7 @@ abstract class ES_Background_Process extends WP_Background_Process {
155
  * Schedule cron healthcheck.
156
  *
157
  * @param array $schedules Schedules.
 
158
  * @return array
159
  */
160
  public function schedule_cron_healthcheck( $schedules ) {
93
  */
94
  protected function handle() {
95
  $this->lock_process();
96
+ $logger = get_ig_logger();
97
 
98
  do {
99
  $batch = $this->get_batch();
100
 
101
+ $logger->info( '--------------------- Started To Run Task Again---------------------', array( 'source' => 'es_update' ) );
102
  foreach ( $batch->data as $key => $value ) {
103
+ $task_transient = $value . '_processed';
104
+
105
+ $task = false; // By default it's set to false
106
+ $logger->info( '-------- Checking Transient For: ' . $value, array( 'source' => 'es_update' ) );
107
+ if ( false === get_transient( $task_transient ) ) {
108
+ $logger->info( '------- Running Task: >>>>> ' . $value, array( 'source' => 'es_update' ) );
109
+ $task = $this->task( $value );
110
+ $logger->info( '------ Task Completed: >>>>> ' . $value . ' data ' . print_r( $task, true ), array( 'source' => 'es_update' ) );
111
+ }
112
 
113
  if ( false !== $task ) {
114
  $batch->data[ $key ] = $task;
115
  } else {
116
+ $logger->info( '---- Setting Transient For: ' . $value, array( 'source' => 'es_update' ) );
117
+ set_transient( $task_transient, true, MINUTE_IN_SECONDS * 100 );
118
  unset( $batch->data[ $key ] );
119
  }
120
 
155
  $memory_limit = '128M';
156
  }
157
 
158
+ if ( ! $memory_limit || - 1 === intval( $memory_limit ) ) {
159
  // Unlimited, set to 32GB.
160
  $memory_limit = '32G';
161
  }
167
  * Schedule cron healthcheck.
168
  *
169
  * @param array $schedules Schedules.
170
+ *
171
  * @return array
172
  */
173
  public function schedule_cron_healthcheck( $schedules ) {
includes/upgrade/class-es-background-updater.php CHANGED
@@ -108,9 +108,6 @@ class ES_Background_Updater extends ES_Background_Process {
108
 
109
  $result = (bool) call_user_func( $callback, $this );
110
 
111
- // Say goodbye to the same task. We don't want to run the same task again.
112
- $result = false;
113
-
114
  if ( $result ) {
115
  $logger->info( sprintf( '%s callback needs to run again', $callback ), array( 'source' => 'ig_es_db_updates' ) );
116
  } else {
@@ -141,6 +138,14 @@ class ES_Background_Updater extends ES_Background_Process {
141
  * @return bool
142
  */
143
  public function is_memory_exceeded() {
144
- return $this->memory_exceeded();
 
 
 
 
 
 
 
 
145
  }
146
  }
108
 
109
  $result = (bool) call_user_func( $callback, $this );
110
 
 
 
 
111
  if ( $result ) {
112
  $logger->info( sprintf( '%s callback needs to run again', $callback ), array( 'source' => 'ig_es_db_updates' ) );
113
  } else {
138
  * @return bool
139
  */
140
  public function is_memory_exceeded() {
141
+ return false;
142
+
143
+ //return $this->memory_exceeded();
144
+ }
145
+
146
+ public function time_exceeded() {
147
+ return false;
148
+
149
+ // return $this->time_exceeded();
150
  }
151
  }
includes/upgrade/es-update-functions.php CHANGED
@@ -420,6 +420,27 @@ function ig_es_update_3516_db_version() {
420
 
421
 
422
  /* --------------------- ES 4.0.0 (Start)--------------------------- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  function ig_es_update_400_create_tables() {
424
  global $wpdb;
425
 
@@ -427,7 +448,6 @@ function ig_es_update_400_create_tables() {
427
  require_once ABSPATH . 'wp-admin/includes/upgrade.php';
428
  dbDelta( ES_Install::get_ig_es_400_schema() );
429
 
430
-
431
  }
432
 
433
  function ig_es_update_400_import_options() {
420
 
421
 
422
  /* --------------------- ES 4.0.0 (Start)--------------------------- */
423
+ function ig_es_update_400_delete_tables() {
424
+ global $wpdb;
425
+
426
+ $tables_to_delete = array(
427
+ $wpdb->prefix . 'ig_blocked_emails',
428
+ $wpdb->prefix . 'ig_campaigns',
429
+ //$wpdb->prefix . 'ig_contacts',
430
+ $wpdb->prefix . 'ig_contacts_ips',
431
+ $wpdb->prefix . 'ig_forms',
432
+ $wpdb->prefix . 'ig_lists',
433
+ $wpdb->prefix . 'ig_lists_contacts',
434
+ $wpdb->prefix . 'ig_mailing_queue',
435
+ $wpdb->prefix . 'ig_sending_queue'
436
+ );
437
+
438
+ foreach ($tables_to_delete as $table) {
439
+ $query = "DROP TABLE IF EXISTS {$table}";
440
+ $wpdb->query($query);
441
+ }
442
+ }
443
+
444
  function ig_es_update_400_create_tables() {
445
  global $wpdb;
446
 
448
  require_once ABSPATH . 'wp-admin/includes/upgrade.php';
449
  dbDelta( ES_Install::get_ig_es_400_schema() );
450
 
 
451
  }
452
 
453
  function ig_es_update_400_import_options() {
public/partials/class-es-shortcode.php CHANGED
@@ -122,7 +122,7 @@ class ES_Shortcode {
122
  $list_ids = ! empty( $data['lists'] ) ? $data['lists'] : array();
123
  $form_id = ! empty( $data['form_id'] ) ? $data['form_id'] : 0;
124
  $list = ! empty( $data['list'] ) ? $data['list'] : 0;
125
- $desc = ! empty( $data['desc'] ) ? $data['desc'] : 0;
126
  //replace total contact
127
  $total_contacts = ES_DB_Contacts::count_active_subscribers_by_list_id();
128
  $desc = str_replace( "{{TOTAL-CONTACTS}}", $total_contacts, $desc );
122
  $list_ids = ! empty( $data['lists'] ) ? $data['lists'] : array();
123
  $form_id = ! empty( $data['form_id'] ) ? $data['form_id'] : 0;
124
  $list = ! empty( $data['list'] ) ? $data['list'] : 0;
125
+ $desc = ! empty( $data['desc'] ) ? $data['desc'] : '';
126
  //replace total contact
127
  $total_contacts = ES_DB_Contacts::count_active_subscribers_by_list_id();
128
  $desc = str_replace( "{{TOTAL-CONTACTS}}", $total_contacts, $desc );
readme.txt CHANGED
@@ -5,7 +5,7 @@ Author URI: https://www.icegram.com/
5
  Tags: subscription, newsletter, email marketing, post notification, email newsletter form, email signup, email widget, newsletter signup, subscribe, subscription form, bulk emails, signup form, list builder, lead generation, welcome email, contacts
6
  Requires at least: 3.9
7
  Tested up to: 5.1.1
8
- Stable tag: 4.0.17
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses
11
 
@@ -301,12 +301,18 @@ Use our free plugin [Email Subscribers - Group Selector](https://wordpress.org/p
301
 
302
  == Changelog ==
303
 
 
 
 
 
 
 
304
  = 4.0.17 (03.05.2019) =
305
- * New : New keywords added : {{TOTAL-CONTACTC}} in form description
306
  * Fix : Post/page editor issue with RTL sites
307
 
308
  = 4.0.16 (23.04.2019) =
309
- * New : New keywords added : {{TOTAL-CONTACTC}} , {{SITEURL}}, {{SITENAME}}
310
  * New : Added option to enable/ disable WordPress Cron for Email Subscribers
311
  * New : Added option to enable/ disable email open tracking
312
  * Fix : Database error while sorting list
5
  Tags: subscription, newsletter, email marketing, post notification, email newsletter form, email signup, email widget, newsletter signup, subscribe, subscription form, bulk emails, signup form, list builder, lead generation, welcome email, contacts
6
  Requires at least: 3.9
7
  Tested up to: 5.1.1
8
+ Stable tag: 4.0.18
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses
11
 
301
 
302
  == Changelog ==
303
 
304
+ = 4.0.18 (07.05.2019) =
305
+ * New : Added a feature to duplicate any template
306
+ * New : Added support to re run database migration from ES 3.5.18
307
+ * Fix : Display "0" above form
308
+ * Fix : Migration issue
309
+
310
  = 4.0.17 (03.05.2019) =
311
+ * New : New keywords added : {{TOTAL-CONTACTS}} in form description
312
  * Fix : Post/page editor issue with RTL sites
313
 
314
  = 4.0.16 (23.04.2019) =
315
+ * New : New keywords added : {{TOTAL-CONTACTS}} , {{SITEURL}}, {{SITENAME}}
316
  * New : Added option to enable/ disable WordPress Cron for Email Subscribers
317
  * New : Added option to enable/ disable email open tracking
318
  * Fix : Database error while sorting list