Email Subscribers & Newsletters - Version 4.6.5

Version Description

Download this release

Release Info

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

Code changes from version 4.6.4 to 4.6.5

Files changed (30) hide show
  1. email-subscribers.php +3 -3
  2. lite/admin/js/email-subscribers-admin.js +2 -0
  3. lite/includes/class-email-subscribers.php +24 -65
  4. lite/includes/class-es-common.php +41 -1
  5. lite/includes/class-es-install.php +61 -0
  6. lite/includes/classes/class-es-contacts-table.php +19 -3
  7. lite/includes/classes/class-es-cron.php +9 -0
  8. lite/includes/classes/class-es-import-subscribers.php +9 -9
  9. lite/includes/classes/class-es-reports-table.php +70 -4
  10. lite/includes/classes/class-ig-es-wc-session-tracker.php +304 -0
  11. lite/includes/classes/ig-es-wc-cookies.php +52 -0
  12. lite/includes/db/class-es-db-actions.php +33 -0
  13. lite/includes/db/class-es-db-contacts.php +17 -9
  14. lite/includes/db/class-es-db-lists-contacts.php +5 -0
  15. lite/includes/db/class-ig-es-db-wc-cart.php +208 -0
  16. lite/includes/db/class-ig-es-db-wc-guest.php +196 -0
  17. lite/includes/es-core-functions.php +20 -0
  18. lite/includes/pro-features.php +1 -1
  19. lite/includes/upgrade/es-update-functions.php +20 -0
  20. lite/includes/workflows/admin/class-es-workflow-admin-edit.php +7 -1
  21. lite/includes/workflows/admin/views/meta-box-trigger.php +3 -1
  22. lite/includes/workflows/admin/views/trigger-fields.php +2 -2
  23. lite/includes/workflows/class-es-workflow-data-layer.php +28 -0
  24. lite/includes/workflows/class-es-workflow-query.php +39 -0
  25. lite/includes/workflows/class-es-workflow.php +0 -1
  26. lite/includes/workflows/db/class-es-db-workflows.php +9 -0
  27. lite/includes/workflows/queue/class-es-workflow-queue.php +5 -5
  28. lite/includes/workflows/workflow-helper-functions.php +31 -0
  29. lite/public/js/email-subscribers-public.js +1 -0
  30. readme.txt +9 -2
email-subscribers.php CHANGED
@@ -3,11 +3,11 @@
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.6.4
7
  * Author: Icegram
8
  * Author URI: https://www.icegram.com/
9
  * Requires at least: 3.9
10
- * Tested up to: 5.5.3
11
  * WC requires at least: 3.6.0
12
  * WC tested up to: 4.6.2
13
  * Requires PHP: 5.6
@@ -177,7 +177,7 @@ if ( 'premium' === $ig_es_plan ) {
177
  /* ***************************** Initial Compatibility Work (End) ******************* */
178
 
179
  if ( ! defined( 'ES_PLUGIN_VERSION' ) ) {
180
- define( 'ES_PLUGIN_VERSION', '4.6.4' );
181
  }
182
 
183
  // Plugin Folder Path.
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.6.5
7
  * Author: Icegram
8
  * Author URI: https://www.icegram.com/
9
  * Requires at least: 3.9
10
+ * Tested up to: 5.6
11
  * WC requires at least: 3.6.0
12
  * WC tested up to: 4.6.2
13
  * Requires PHP: 5.6
177
  /* ***************************** Initial Compatibility Work (End) ******************* */
178
 
179
  if ( ! defined( 'ES_PLUGIN_VERSION' ) ) {
180
+ define( 'ES_PLUGIN_VERSION', '4.6.5' );
181
  }
182
 
183
  // Plugin Folder Path.
lite/admin/js/email-subscribers-admin.js CHANGED
@@ -244,6 +244,8 @@
244
  campaign_status(campaign_type);
245
  });
246
 
 
 
247
  function campaign_status( campaign_type ) {
248
  var $status_id = $('#ig_es_filter_campaign_status_by_type');
249
  switch (campaign_type) {
244
  campaign_status(campaign_type);
245
  });
246
 
247
+ $('#ig_es_filter_reports_by_campaign_type option[value="sequence"]').hide();
248
+
249
  function campaign_status( campaign_type ) {
250
  var $status_id = $('#ig_es_filter_campaign_status_by_type');
251
  switch (campaign_type) {
lite/includes/class-email-subscribers.php CHANGED
@@ -646,6 +646,8 @@ if ( ! class_exists( 'Email_Subscribers' ) ) {
646
  'lite/includes/db/class-es-db-forms.php',
647
  'lite/includes/db/class-es-db-blocked-emails.php',
648
  'lite/includes/db/class-es-db-actions.php',
 
 
649
 
650
  // Mailers
651
  'lite/includes/mailers/class-es-base-mailer.php',
@@ -734,46 +736,40 @@ if ( ! class_exists( 'Email_Subscribers' ) ) {
734
  'lite/includes/feedback/class-ig-feedback.php',
735
  'lite/includes/feedback.php',
736
  // End-IG-Code.
737
-
738
- //Load Starter & Pro files if exists
739
- 'starter/class-es-utils.php',
740
- 'pro/pro-class-email-subscribers.php',
741
- 'starter/starter-class-email-subscribers.php',
742
- 'pro/classes/class-es-pro-sequence-report.php',
743
-
744
- 'starter/mailers/class-es-smtp-mailer.php',
745
-
746
  // Workflows
747
  'lite/includes/workflows/db/class-es-db-workflows.php',
748
  'lite/includes/workflows/db/class-es-db-workflows-queue.php',
749
  'lite/includes/workflows/class-es-workflows-table.php',
750
-
751
-
752
  // Workflow Abstracts
753
  'lite/includes/workflows/abstracts/class-es-workflow-registry.php',
754
  'lite/includes/workflows/abstracts/class-es-workflow-trigger.php',
755
  'lite/includes/workflows/abstracts/class-es-workflow-action.php',
756
  'lite/includes/workflows/abstracts/class-es-workflow-data-type.php',
757
-
758
  // Workflow Utility
759
  'lite/includes/workflows/class-es-clean.php',
760
  'lite/includes/workflows/class-es-format.php',
761
  'lite/includes/workflows/class-es-workflow-time-helper.php',
762
  'lite/includes/workflows/class-es-workflow-datetime.php',
763
  'lite/includes/workflows/workflow-helper-functions.php',
764
-
765
  // Workflow
766
  'lite/includes/workflows/class-es-workflow.php',
767
  'lite/includes/workflows/class-es-workflow-factory.php',
768
-
769
  // Data Types
770
  'lite/includes/workflows/data-types/abstracts/class-es-data-type-form-data.php',
771
  'lite/includes/workflows/data-types/class-es-data-type-user.php',
772
  'lite/includes/workflows/class-es-workflow-data-types.php',
773
-
774
  // Data Layer
775
  'lite/includes/workflows/class-es-workflow-data-layer.php',
776
-
777
  // Workflow Fields
778
  'lite/includes/workflows/fields/class-es-field.php',
779
  'lite/includes/workflows/fields/class-es-text.php',
@@ -783,22 +779,22 @@ if ( ! class_exists( 'Email_Subscribers' ) ) {
783
  'lite/includes/workflows/fields/class-es-select.php',
784
  'lite/includes/workflows/fields/class-es-checkbox.php',
785
  'lite/includes/workflows/fields/class-es-wp-editor.php',
786
-
787
  // Workflow Admin
788
  'lite/includes/workflows/admin/class-es-workflow-admin.php',
789
  'lite/includes/workflows/admin/class-es-workflow-admin-edit.php',
790
  'lite/includes/workflows/admin/class-es-workflow-admin-ajax.php',
791
-
792
  // Workflow Triggers.
793
  'lite/includes/workflows/triggers/abstracts/class-es-trigger-form-submitted.php',
794
  'lite/includes/workflows/triggers/class-es-trigger-user-registered.php',
795
  'lite/includes/workflows/triggers/class-es-trigger-user-deleted.php',
796
  'lite/includes/workflows/triggers/class-es-trigger-user-updated.php',
797
  'lite/includes/workflows/class-es-workflow-triggers.php',
798
-
799
  // Abstracts workflow actions
800
  'lite/includes/workflows/actions/abstracts/class-ig-es-action-send-email-abstract.php',
801
-
802
  // Workflow Actions.
803
  'lite/includes/workflows/actions/class-es-action-add-to-list.php',
804
  'lite/includes/workflows/actions/class-es-action-move-contact.php',
@@ -806,67 +802,29 @@ if ( ! class_exists( 'Email_Subscribers' ) ) {
806
  'lite/includes/workflows/actions/class-es-action-delete-contact.php',
807
  'lite/includes/workflows/actions/class-es-action-update-contact.php',
808
  'lite/includes/workflows/class-es-workflow-actions.php',
809
-
810
  // Workflow Query
811
  'lite/includes/workflows/class-es-workflow-query.php',
812
-
813
  // Workflow Queue
814
  'lite/includes/workflows/queue/class-es-workflow-queue.php',
815
  'lite/includes/workflows/queue/class-es-workflow-queue-factory.php',
816
  'lite/includes/workflows/queue/class-es-workflow-queue-handler.php',
817
  'lite/includes/workflows/queue/class-es-workflow-queue-runner.php',
818
 
819
- // Data Types from Starter version
820
- 'starter/workflows/data-types/class-es-data-type-comment.php',
821
- 'starter/workflows/data-types/class-es-data-type-wc-order.php',
822
- 'starter/workflows/data-types/class-es-data-type-edd-payment.php',
823
- 'starter/workflows/data-types/class-es-data-type-cf7-data.php',
824
- 'starter/workflows/data-types/class-es-data-type-wpforms-data.php',
825
- 'starter/workflows/data-types/class-es-data-type-ninja-forms-data.php',
826
- 'starter/workflows/data-types/class-es-data-type-give-data.php',
827
- 'starter/workflows/data-types/class-es-data-type-gravity-forms-data.php',
828
- 'starter/workflows/data-types/class-es-data-type-forminator-forms-data.php',
829
-
830
- // Triggers from Starter version
831
- 'starter/workflows/triggers/class-es-trigger-comment-added.php',
832
- 'starter/workflows/triggers/class-es-trigger-cf7-submitted.php',
833
- 'starter/workflows/triggers/class-es-trigger-wc-order-created.php',
834
- 'starter/workflows/triggers/class-es-trigger-wc-order-completed.php',
835
- 'starter/workflows/triggers/class-es-trigger-edd-purchase-completed.php',
836
- 'starter/workflows/triggers/class-es-trigger-wpforms-submitted.php',
837
- 'starter/workflows/triggers/class-es-trigger-ninja-forms-submitted.php',
838
- 'starter/workflows/triggers/class-es-trigger-give-donation-made.php',
839
- 'starter/workflows/triggers/class-es-trigger-gravity-forms-submitted.php',
840
- 'starter/workflows/triggers/class-es-trigger-forminator-forms-submitted.php',
841
-
842
- // Triggers from Pro version
843
- 'pro/workflows/triggers/class-es-trigger-wc-order-refunded.php',
844
- 'pro/workflows/triggers/class-es-trigger-wc-product-review-approved.php',
845
-
846
- // Trigger Extra fields from Pro version
847
- 'pro/workflows/triggers/extras/class-es-pro-trigger-user-registered.php',
848
-
849
- // Action Extra fields from Pro version
850
- 'pro/workflows/actions/extras/class-es-pro-action-add-to-list.php',
851
-
852
- // Data Types from Pro version
853
- 'pro/workflows/data-types/class-es-data-type-review.php',
854
-
855
- // Actions from Pro version
856
- 'pro/workflows/actions/class-es-action-move-to-list.php',
857
- 'pro/workflows/actions/class-es-action-remove-from-list.php',
858
- 'pro/workflows/actions/class-es-action-send-email.php',
859
-
860
  // Workflow Loader
861
  'lite/includes/workflows/class-es-workflow-loader.php',
862
-
863
  // Premium services ui components.
864
  'lite/includes/premium-services-ui/class-ig-es-premium-services-ui.php',
865
 
866
  // Background Process Helper
867
  'lite/includes/classes/class-ig-es-background-process-helper.php',
 
 
 
868
  );
869
-
870
  foreach ( $files_to_load as $file ) {
871
  if ( is_file( ES_PLUGIN_DIR . $file ) ) {
872
  require_once ES_PLUGIN_DIR . $file;
@@ -1326,6 +1284,7 @@ if ( ! class_exists( 'Email_Subscribers' ) ) {
1326
  self::$instance->cron = new ES_Cron();
1327
  self::$instance->compatibiloty = new ES_Compatibility();
1328
  self::$instance->workflows_db = new ES_DB_Workflows();
 
1329
  self::$instance->trial = new IG_ES_Trial();
1330
 
1331
  // Start-IG-Code.
646
  'lite/includes/db/class-es-db-forms.php',
647
  'lite/includes/db/class-es-db-blocked-emails.php',
648
  'lite/includes/db/class-es-db-actions.php',
649
+ 'lite/includes/db/class-ig-es-db-wc-cart.php',
650
+ 'lite/includes/db/class-ig-es-db-wc-guest.php',
651
 
652
  // Mailers
653
  'lite/includes/mailers/class-es-base-mailer.php',
736
  'lite/includes/feedback/class-ig-feedback.php',
737
  'lite/includes/feedback.php',
738
  // End-IG-Code.
739
+
740
+ // WC session tracking
741
+ 'lite/includes/classes/class-ig-es-wc-session-tracker.php',
742
+ 'lite/includes/classes/ig-es-wc-cookies.php',
743
+
 
 
 
 
744
  // Workflows
745
  'lite/includes/workflows/db/class-es-db-workflows.php',
746
  'lite/includes/workflows/db/class-es-db-workflows-queue.php',
747
  'lite/includes/workflows/class-es-workflows-table.php',
 
 
748
  // Workflow Abstracts
749
  'lite/includes/workflows/abstracts/class-es-workflow-registry.php',
750
  'lite/includes/workflows/abstracts/class-es-workflow-trigger.php',
751
  'lite/includes/workflows/abstracts/class-es-workflow-action.php',
752
  'lite/includes/workflows/abstracts/class-es-workflow-data-type.php',
753
+
754
  // Workflow Utility
755
  'lite/includes/workflows/class-es-clean.php',
756
  'lite/includes/workflows/class-es-format.php',
757
  'lite/includes/workflows/class-es-workflow-time-helper.php',
758
  'lite/includes/workflows/class-es-workflow-datetime.php',
759
  'lite/includes/workflows/workflow-helper-functions.php',
760
+
761
  // Workflow
762
  'lite/includes/workflows/class-es-workflow.php',
763
  'lite/includes/workflows/class-es-workflow-factory.php',
764
+
765
  // Data Types
766
  'lite/includes/workflows/data-types/abstracts/class-es-data-type-form-data.php',
767
  'lite/includes/workflows/data-types/class-es-data-type-user.php',
768
  'lite/includes/workflows/class-es-workflow-data-types.php',
769
+
770
  // Data Layer
771
  'lite/includes/workflows/class-es-workflow-data-layer.php',
772
+
773
  // Workflow Fields
774
  'lite/includes/workflows/fields/class-es-field.php',
775
  'lite/includes/workflows/fields/class-es-text.php',
779
  'lite/includes/workflows/fields/class-es-select.php',
780
  'lite/includes/workflows/fields/class-es-checkbox.php',
781
  'lite/includes/workflows/fields/class-es-wp-editor.php',
782
+
783
  // Workflow Admin
784
  'lite/includes/workflows/admin/class-es-workflow-admin.php',
785
  'lite/includes/workflows/admin/class-es-workflow-admin-edit.php',
786
  'lite/includes/workflows/admin/class-es-workflow-admin-ajax.php',
787
+
788
  // Workflow Triggers.
789
  'lite/includes/workflows/triggers/abstracts/class-es-trigger-form-submitted.php',
790
  'lite/includes/workflows/triggers/class-es-trigger-user-registered.php',
791
  'lite/includes/workflows/triggers/class-es-trigger-user-deleted.php',
792
  'lite/includes/workflows/triggers/class-es-trigger-user-updated.php',
793
  'lite/includes/workflows/class-es-workflow-triggers.php',
794
+
795
  // Abstracts workflow actions
796
  'lite/includes/workflows/actions/abstracts/class-ig-es-action-send-email-abstract.php',
797
+
798
  // Workflow Actions.
799
  'lite/includes/workflows/actions/class-es-action-add-to-list.php',
800
  'lite/includes/workflows/actions/class-es-action-move-contact.php',
802
  'lite/includes/workflows/actions/class-es-action-delete-contact.php',
803
  'lite/includes/workflows/actions/class-es-action-update-contact.php',
804
  'lite/includes/workflows/class-es-workflow-actions.php',
805
+
806
  // Workflow Query
807
  'lite/includes/workflows/class-es-workflow-query.php',
808
+
809
  // Workflow Queue
810
  'lite/includes/workflows/queue/class-es-workflow-queue.php',
811
  'lite/includes/workflows/queue/class-es-workflow-queue-factory.php',
812
  'lite/includes/workflows/queue/class-es-workflow-queue-handler.php',
813
  'lite/includes/workflows/queue/class-es-workflow-queue-runner.php',
814
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
815
  // Workflow Loader
816
  'lite/includes/workflows/class-es-workflow-loader.php',
817
+
818
  // Premium services ui components.
819
  'lite/includes/premium-services-ui/class-ig-es-premium-services-ui.php',
820
 
821
  // Background Process Helper
822
  'lite/includes/classes/class-ig-es-background-process-helper.php',
823
+
824
+ 'starter/starter-class-email-subscribers.php',
825
+ 'pro/pro-class-email-subscribers.php',
826
  );
827
+
828
  foreach ( $files_to_load as $file ) {
829
  if ( is_file( ES_PLUGIN_DIR . $file ) ) {
830
  require_once ES_PLUGIN_DIR . $file;
1284
  self::$instance->cron = new ES_Cron();
1285
  self::$instance->compatibiloty = new ES_Compatibility();
1286
  self::$instance->workflows_db = new ES_DB_Workflows();
1287
+ self::$instance->carts_db = new IG_ES_DB_WC_Cart();
1288
  self::$instance->trial = new IG_ES_Trial();
1289
 
1290
  // Start-IG-Code.
lite/includes/class-es-common.php CHANGED
@@ -123,9 +123,12 @@ class ES_Common {
123
  require_once ABSPATH . 'wp-includes/class-wp-oembed.php';
124
  }
125
 
 
 
 
126
  $oembed = new WP_oEmbed();
127
  $provider = $oembed->discover( $url );
128
-
129
  if ( ! empty( $provider ) ) {
130
  $oembed_response = $oembed->fetch( $provider, $url, $attr );
131
  if ( is_object( $oembed_response ) && ! empty( $oembed_response->type ) && 'video' === $oembed_response->type ) {
@@ -169,6 +172,8 @@ class ES_Common {
169
  }
170
  }
171
  }
 
 
172
  }
173
 
174
  return $html;
@@ -1815,5 +1820,40 @@ class ES_Common {
1815
  return $message_html;
1816
  }
1817
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1818
  }
1819
 
123
  require_once ABSPATH . 'wp-includes/class-wp-oembed.php';
124
  }
125
 
126
+ // Increase http response size to allow fetching of youtube pages which has large page size.
127
+ add_filter( 'http_request_args', 'ig_es_increase_http_response_size' );
128
+
129
  $oembed = new WP_oEmbed();
130
  $provider = $oembed->discover( $url );
131
+
132
  if ( ! empty( $provider ) ) {
133
  $oembed_response = $oembed->fetch( $provider, $url, $attr );
134
  if ( is_object( $oembed_response ) && ! empty( $oembed_response->type ) && 'video' === $oembed_response->type ) {
172
  }
173
  }
174
  }
175
+
176
+ remove_filter( 'http_request_args', 'ig_es_increase_http_response_size' );
177
  }
178
 
179
  return $html;
1820
  return $message_html;
1821
  }
1822
  }
1823
+
1824
+ /**
1825
+ * Prepare Campaign Report Status dropdown
1826
+ *
1827
+ * @param string $selected
1828
+ * @param string $default_label
1829
+ *
1830
+ * @return string
1831
+ *
1832
+ * @since 4.6.5
1833
+ */
1834
+ public static function prepare_campaign_report_statuses_dropdown_options( $selected = '', $default_label = '' ) {
1835
+
1836
+ $statuses = array(
1837
+ 'Sent' => __( 'Completed', 'email-subscribers' ),
1838
+ 'In Queue' => __( 'In Queue', 'email-subscribers' ),
1839
+ 'Sending' => __( 'Sending', 'email-subscribers' ),
1840
+ );
1841
+
1842
+ $dropdown = '<option class="text-sm" value="">' . esc_html__( 'All Status', 'email-subscribers' ) . '</option>';
1843
+
1844
+ foreach ( $statuses as $key => $status ) {
1845
+
1846
+ $dropdown .= '<option class="text-sm" value="' . esc_attr( $key ) . '" ';
1847
+
1848
+ if ( strtolower( $selected ) === strtolower( $key ) ) {
1849
+ $dropdown .= 'selected = selected';
1850
+ }
1851
+
1852
+ $dropdown .= '>' . esc_html( $status ) . '</option>';
1853
+ }
1854
+
1855
+ return $dropdown;
1856
+ }
1857
+
1858
  }
1859
 
lite/includes/class-es-install.php CHANGED
@@ -231,6 +231,11 @@ if ( ! class_exists( 'ES_Install' ) ) {
231
  'ig_es_update_463_db_version',
232
  ),
233
 
 
 
 
 
 
234
  );
235
 
236
  /**
@@ -1164,6 +1169,61 @@ if ( ! class_exists( 'ES_Install' ) ) {
1164
  return $tables;
1165
  }
1166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1167
  /**
1168
  * Collect multiple version schema
1169
  *
@@ -1180,6 +1240,7 @@ if ( ! class_exists( 'ES_Install' ) ) {
1180
  $tables .= self::get_ig_es_421_schema( $collate );
1181
  $tables .= self::get_ig_es_424_schema( $collate );
1182
  $tables .= self::get_ig_es_441_schema( $collate );
 
1183
 
1184
  return $tables;
1185
  }
231
  'ig_es_update_463_db_version',
232
  ),
233
 
234
+ '4.6.5' => array(
235
+ 'ig_es_update_465_create_tables',
236
+ 'ig_es_update_465_db_version',
237
+ ),
238
+
239
  );
240
 
241
  /**
1169
  return $tables;
1170
  }
1171
 
1172
+ /**
1173
+ * Create Links Table
1174
+ *
1175
+ * @param string $collate
1176
+ *
1177
+ * @return string
1178
+ *
1179
+ * @sinc 4.4.1
1180
+ */
1181
+ public static function get_ig_es_465_schema( $collate = '' ) {
1182
+ global $wpdb;
1183
+
1184
+ $tables = "CREATE TABLE `{$wpdb->prefix}ig_wc_cart` (
1185
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
1186
+ `status` varchar(100) NOT NULL default '',
1187
+ `user_id` bigint(20) NOT NULL default 0,
1188
+ `guest_id` bigint(20) NOT NULL default 0,
1189
+ `last_modified` datetime NULL,
1190
+ `created` datetime NULL,
1191
+ `items` longtext NOT NULL default '',
1192
+ `coupons` longtext NOT NULL default '',
1193
+ `fees` longtext NOT NULL default '',
1194
+ `shipping_tax_total` double DEFAULT 0 NOT NULL,
1195
+ `shipping_total` double DEFAULT 0 NOT NULL,
1196
+ `total` double DEFAULT 0 NOT NULL,
1197
+ `token` varchar(32) NOT NULL default '',
1198
+ `currency` varchar(8) NOT NULL default '',
1199
+ PRIMARY KEY (id),
1200
+ KEY `status` (`status`),
1201
+ KEY `user_id` (`user_id`),
1202
+ KEY `guest_id` (`guest_id`),
1203
+ KEY `last_modified` (`last_modified`),
1204
+ KEY `created` (`created`)
1205
+ ) $collate;
1206
+
1207
+ CREATE TABLE `{$wpdb->prefix}ig_wc_guests` (
1208
+ id bigint(20) NOT NULL AUTO_INCREMENT,
1209
+ email varchar(255) NOT NULL default '',
1210
+ tracking_key varchar(32) NOT NULL default '',
1211
+ created datetime NULL,
1212
+ last_active datetime NULL,
1213
+ language varchar(10) NOT NULL default '',
1214
+ most_recent_order bigint(20) NOT NULL DEFAULT 0,
1215
+ version bigint(20) NOT NULL default 0,
1216
+ PRIMARY KEY (id),
1217
+ KEY tracking_key (tracking_key),
1218
+ KEY email (email(191)),
1219
+ KEY most_recent_order (most_recent_order),
1220
+ KEY version (version)
1221
+ ) $collate;
1222
+ ";
1223
+
1224
+ return $tables;
1225
+ }
1226
+
1227
  /**
1228
  * Collect multiple version schema
1229
  *
1240
  $tables .= self::get_ig_es_421_schema( $collate );
1241
  $tables .= self::get_ig_es_424_schema( $collate );
1242
  $tables .= self::get_ig_es_441_schema( $collate );
1243
+ $tables .= self::get_ig_es_465_schema( $collate );
1244
 
1245
  return $tables;
1246
  }
lite/includes/classes/class-es-contacts-table.php CHANGED
@@ -47,6 +47,15 @@ class ES_Contacts_Table extends WP_List_Table {
47
  */
48
  public $lists_id_name_map = array();
49
 
 
 
 
 
 
 
 
 
 
50
  /**
51
  * Contacts database object
52
  *
@@ -868,7 +877,8 @@ class ES_Contacts_Table extends WP_List_Table {
868
  case 'first_name':
869
  case 'email':
870
  default:
871
- return $item[ $column_name ]; //Show the whole array for troubleshooting purposes
 
872
  }
873
  }
874
 
@@ -1015,9 +1025,12 @@ class ES_Contacts_Table extends WP_List_Table {
1015
  'name' => __( 'Name', 'email-subscribers' ),
1016
  'email' => __( 'Email', 'email-subscribers' ),
1017
  'lists' => __( 'List(s)', 'email-subscribers' ),
1018
- 'ip' => __( 'IP', 'email-subscribers' ),
1019
- 'created_at' => __( 'Created', 'email-subscribers' ),
1020
  );
 
 
 
 
 
1021
 
1022
  return $columns;
1023
  }
@@ -1149,6 +1162,9 @@ class ES_Contacts_Table extends WP_List_Table {
1149
  $this->contact_lists_statuses = $contact_lists_statuses;
1150
 
1151
  $this->lists_id_name_map = ES()->lists_db->get_list_id_name_map();
 
 
 
1152
  }
1153
  }
1154
 
47
  */
48
  public $lists_id_name_map = array();
49
 
50
+ /**
51
+ * Last opened at
52
+ *
53
+ * @since 4.6.5
54
+ * @var array
55
+ *
56
+ */
57
+ public $items_data = array();
58
+
59
  /**
60
  * Contacts database object
61
  *
877
  case 'first_name':
878
  case 'email':
879
  default:
880
+ $column_data = isset( $item[ $column_name ] ) ? $item[ $column_name ] : '-';
881
+ return apply_filters( 'ig_es_contact_column_data', $column_data, $column_name, $item, $this );
882
  }
883
  }
884
 
1025
  'name' => __( 'Name', 'email-subscribers' ),
1026
  'email' => __( 'Email', 'email-subscribers' ),
1027
  'lists' => __( 'List(s)', 'email-subscribers' ),
 
 
1028
  );
1029
+ $can_track_ip = apply_filters( 'ig_es_can_track_subscriber_ip', 'yes' );
1030
+ if ( 'yes' === $can_track_ip ) {
1031
+ $columns['ip'] = __( 'IP', 'email-subscribers' );
1032
+ }
1033
+ $columns['created_at'] = __( 'Created', 'email-subscribers' );
1034
 
1035
  return $columns;
1036
  }
1162
  $this->contact_lists_statuses = $contact_lists_statuses;
1163
 
1164
  $this->lists_id_name_map = ES()->lists_db->get_list_id_name_map();
1165
+
1166
+ $this->items_data = apply_filters( 'ig_es_subscribers_add_col_data', array(), $contact_ids );
1167
+
1168
  }
1169
  }
1170
 
lite/includes/classes/class-es-cron.php CHANGED
@@ -126,6 +126,10 @@ class ES_Cron {
126
  wp_schedule_event( floor( time() / 300 ) * 300, 'ig_es_cron_interval', 'ig_es_cron_worker' );
127
  }
128
 
 
 
 
 
129
  }
130
 
131
  /**
@@ -239,6 +243,11 @@ class ES_Cron {
239
  'display' => esc_html__( 'Email Subscribers Cronjob Interval', 'email-subscribers' ),
240
  );
241
 
 
 
 
 
 
242
  return $schedules;
243
  }
244
 
126
  wp_schedule_event( floor( time() / 300 ) * 300, 'ig_es_cron_interval', 'ig_es_cron_worker' );
127
  }
128
 
129
+ if ( ! wp_next_scheduled( 'ig_es_wc_abandoned_cart_worker' ) ) {
130
+ wp_schedule_event( floor( time() / 300 ) * 300, 'ig_es_two_minutes', 'ig_es_wc_abandoned_cart_worker' );
131
+ }
132
+
133
  }
134
 
135
  /**
243
  'display' => esc_html__( 'Email Subscribers Cronjob Interval', 'email-subscribers' ),
244
  );
245
 
246
+ $schedules['ig_es_two_minutes'] = array(
247
+ 'interval' => 120,
248
+ 'display' => esc_html__( 'Two minutes', 'email-subscribers' ),
249
+ );
250
+
251
  return $schedules;
252
  }
253
 
lite/includes/classes/class-es-import-subscribers.php CHANGED
@@ -315,15 +315,15 @@ class ES_Import_Subscribers {
315
  <td class="w-9/12 pb-3">
316
  <?php
317
  // Allow multiselect for lists field in the pro version by changing list field's class,name and adding multiple attribute.
318
- if ( ES()->is_pro() ) {
319
- $select_list_attr = 'multiple="multiple"';
320
- $select_list_name = 'list_id[]';
321
- $select_list_class = 'ig-es-form-multiselect';
322
- } else {
323
- $select_list_attr = '';
324
- $select_list_name = 'list_id';
325
- $select_list_class = 'form-select';
326
- }
327
  ?>
328
  <div class="ml-12">
329
  <select name="<?php echo esc_attr( $select_list_name ); ?>" id="list_id" class="relative shadow-sm border border-gray-400 sm:w-32 lg:w-48 <?php echo esc_attr( $select_list_class ); ?>" <?php echo esc_attr( $select_list_attr ); ?>>
315
  <td class="w-9/12 pb-3">
316
  <?php
317
  // Allow multiselect for lists field in the pro version by changing list field's class,name and adding multiple attribute.
318
+ if ( ES()->is_pro() ) {
319
+ $select_list_attr = 'multiple="multiple"';
320
+ $select_list_name = 'list_id[]';
321
+ $select_list_class = 'ig-es-form-multiselect';
322
+ } else {
323
+ $select_list_attr = '';
324
+ $select_list_name = 'list_id';
325
+ $select_list_class = 'form-select';
326
+ }
327
  ?>
328
  <div class="ml-12">
329
  <select name="<?php echo esc_attr( $select_list_name ); ?>" id="list_id" class="relative shadow-sm border border-gray-400 sm:w-32 lg:w-48 <?php echo esc_attr( $select_list_class ); ?>" <?php echo esc_attr( $select_list_attr ); ?>>
lite/includes/classes/class-es-reports-table.php CHANGED
@@ -413,6 +413,10 @@ class ES_Reports_Table extends WP_List_Table {
413
  /** Process bulk action */
414
  $this->process_bulk_action();
415
 
 
 
 
 
416
  $per_page = $this->get_items_per_page( 'reports_per_page', 20 );
417
  $current_page = $this->get_pagenum();
418
  $total_items = $this->get_notifications( 0, 0, true );
@@ -428,11 +432,14 @@ class ES_Reports_Table extends WP_List_Table {
428
  }
429
 
430
  public function get_notifications( $per_page = 5, $page_number = 1, $do_count_only = false ) {
431
- global $wpbd;
432
 
433
- $order_by = sanitize_sql_orderby( ig_es_get_request_data( 'orderby' ) );
434
- $order = ig_es_get_request_data( 'order' );
435
- $campaign_id = ig_es_get_request_data( 'campaign_id' );
 
 
 
436
 
437
  $ig_mailing_queue_table = IG_MAILING_QUEUE_TABLE;
438
 
@@ -444,6 +451,7 @@ class ES_Reports_Table extends WP_List_Table {
444
 
445
  $where_columns = array();
446
  $where_args = array();
 
447
 
448
  if ( ! empty( $campaign_id ) && is_numeric( $campaign_id ) ) {
449
  $where_columns[] = 'campaign_id = %d';
@@ -458,6 +466,24 @@ class ES_Reports_Table extends WP_List_Table {
458
 
459
  if ( ! empty( $where_query ) ) {
460
  $sql .= ' WHERE ' . $where_query;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  }
462
 
463
  if ( ! $do_count_only ) {
@@ -590,6 +616,46 @@ class ES_Reports_Table extends WP_List_Table {
590
 
591
  }
592
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
593
  public static function get_instance() {
594
  if ( ! isset( self::$instance ) ) {
595
  self::$instance = new self();
413
  /** Process bulk action */
414
  $this->process_bulk_action();
415
 
416
+ // Search box
417
+ $search = ig_es_get_request_data( 's' );
418
+ $this->search_box( $search, 'reports-search-input' );
419
+
420
  $per_page = $this->get_items_per_page( 'reports_per_page', 20 );
421
  $current_page = $this->get_pagenum();
422
  $total_items = $this->get_notifications( 0, 0, true );
432
  }
433
 
434
  public function get_notifications( $per_page = 5, $page_number = 1, $do_count_only = false ) {
435
+ global $wpdb,$wpbd;
436
 
437
+ $order_by = sanitize_sql_orderby( ig_es_get_request_data( 'orderby' ) );
438
+ $order = ig_es_get_request_data( 'order' );
439
+ $campaign_id = ig_es_get_request_data( 'campaign_id' );
440
+ $search = ig_es_get_request_data( 's' );
441
+ $filter_reports_by_campaign_status = ig_es_get_request_data( 'filter_reports_by_status' );
442
+ $filter_reports_by_campaign_type = ig_es_get_request_data( 'filter_reports_by_campaign_type' );
443
 
444
  $ig_mailing_queue_table = IG_MAILING_QUEUE_TABLE;
445
 
451
 
452
  $where_columns = array();
453
  $where_args = array();
454
+ $add_where_clause = true;
455
 
456
  if ( ! empty( $campaign_id ) && is_numeric( $campaign_id ) ) {
457
  $where_columns[] = 'campaign_id = %d';
466
 
467
  if ( ! empty( $where_query ) ) {
468
  $sql .= ' WHERE ' . $where_query;
469
+ $add_where_clause = false;
470
+ }
471
+
472
+ if ( ! empty( $filter_reports_by_campaign_status ) || ( '0' === $filter_reports_by_campaign_status ) ) {
473
+ if ( ! $add_where_clause ) {
474
+ $sql .= $wpdb->prepare( ' AND status = %s', $filter_reports_by_campaign_status );
475
+ } else {
476
+ $sql .= $wpdb->prepare( ' WHERE status = %s', $filter_reports_by_campaign_status );
477
+ $add_where_clause = false;
478
+ }
479
+ }
480
+
481
+ if ( ! empty( $filter_reports_by_campaign_type ) ) {
482
+ if ( ! $add_where_clause ) {
483
+ $sql .= $wpdb->prepare( ' AND meta LIKE %s', '%' . $wpdb->esc_like( $filter_reports_by_campaign_type ) . '%' );
484
+ } else {
485
+ $sql .= $wpdb->prepare( ' WHERE meta LIKE %s', '%' . $wpdb->esc_like( $filter_reports_by_campaign_type ) . '%' );
486
+ }
487
  }
488
 
489
  if ( ! $do_count_only ) {
616
 
617
  }
618
 
619
+ /**
620
+ * Prepare search box
621
+ *
622
+ * @param string $text
623
+ * @param string $input_id
624
+ *
625
+ * @since 4.6.5
626
+ */
627
+ public function search_box( $text = '', $input_id = '' ) {
628
+ ?>
629
+ <p class="search-box">
630
+ <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo esc_attr( $text ); ?>:</label>
631
+ <input type="search" id="<?php echo esc_attr( $input_id ); ?>" name="s" value="<?php _admin_search_query(); ?>"/>
632
+ <?php submit_button( __( 'Search Reports', 'email-subscribers' ), 'button', false, false, array( 'id' => 'search-submit' ) ); ?>
633
+ </p>
634
+ <p class="search-box search-group-box box-ma10">
635
+ <?php
636
+ $filter_by_status = ig_es_get_request_data( 'filter_reports_by_status' );
637
+ ?>
638
+ <select name="filter_reports_by_status" id="ig_es_filter_report_by_status">
639
+ <?php
640
+ $allowedtags = ig_es_allowed_html_tags_in_esc();
641
+ add_filter( 'safe_style_css', 'ig_es_allowed_css_style' );
642
+ $campaign_report_status = ES_Common::prepare_campaign_report_statuses_dropdown_options( $filter_by_status, __( 'All Status', 'email-subscribers' ) );
643
+ echo wp_kses( $campaign_report_status , $allowedtags );
644
+ ?>
645
+ </select>
646
+ </p>
647
+ <p class="search-box search-group-box box-ma10">
648
+ <?php $filter_by_campaign_type = ig_es_get_request_data( 'filter_reports_by_campaign_type' ); ?>
649
+ <select name="filter_reports_by_campaign_type" id="ig_es_filter_reports_by_campaign_type">
650
+ <?php
651
+ $campaign_report_type = ES_Common::prepare_campaign_type_dropdown_options( $filter_by_campaign_type, __( 'All Type', 'email-subscribers' ) );
652
+ echo wp_kses( $campaign_report_type , $allowedtags );
653
+ ?>
654
+ </select>
655
+ </p>
656
+ <?php
657
+ }
658
+
659
  public static function get_instance() {
660
  if ( ! isset( self::$instance ) ) {
661
  self::$instance = new self();
lite/includes/classes/class-ig-es-wc-session-tracker.php ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) {
3
+ exit;
4
+ }
5
+
6
+ /**
7
+ * Tracks logged out customers via cookies.
8
+ *
9
+ * @class IG_ES_WC_Session_Tracker
10
+ *
11
+ */
12
+ class IG_ES_WC_Session_Tracker {
13
+
14
+ /**
15
+ * Tracking cookie expiry
16
+ *
17
+ * @var int (days)
18
+ **/
19
+ private static $tracking_cookie_expiry;
20
+
21
+ /**
22
+ * Tracking cookie name
23
+ * @var string - cookie name
24
+ **/
25
+ private static $tracking_key_cookie_name;
26
+
27
+ /**
28
+ * Tracking key
29
+ *
30
+ * @var string - This key WILL BE saved
31
+ **/
32
+ private static $tracking_key_to_set = '';
33
+
34
+
35
+ /**
36
+ * Returns true if a session tracking cookie has been set.
37
+ *
38
+ * Note: Includes any changes to the cookie in the current request.
39
+ *
40
+ * @since 4.6.5
41
+ *
42
+ * @return bool
43
+ */
44
+ public static function is_tracking_cookie_set() {
45
+ return (bool) IG_ES_WC_Cookies::get( self::$tracking_key_cookie_name );
46
+ }
47
+
48
+
49
+ /**
50
+ * Returns true if a session tracking cookie has been set.
51
+ *
52
+ * Note: Includes any changes to the cookie in the current request.
53
+ *
54
+ * @since 4.2
55
+ *
56
+ * @return bool
57
+ */
58
+ public static function is_session_started_cookie_set() {
59
+ return (bool) IG_ES_WC_Cookies::get( 'wp_ig_es_session_started' );
60
+ }
61
+
62
+
63
+ /**
64
+ * Returns the tracking key as currently stored in the cookie.
65
+ *
66
+ * @since 4.3
67
+ *
68
+ * @return string
69
+ */
70
+ public static function get_tracking_cookie() {
71
+ return ES_Clean::string( IG_ES_WC_Cookies::get( self::$tracking_key_cookie_name ) );
72
+ }
73
+
74
+
75
+ /**
76
+ * This method doesn't actually set the cookie, rather it initiates the cookie setting.
77
+ * Cookies are set only on 'wp', 'shutdown' or 'ig_es/ajax/before_send_json'.
78
+ *
79
+ * @since 4.3
80
+ *
81
+ * @param string $tracking_key
82
+ *
83
+ * @return bool
84
+ */
85
+ public static function set_tracking_key_to_be_set( $tracking_key ) {
86
+ if ( headers_sent() ) {
87
+ return false; // cookies can't be set
88
+ }
89
+
90
+ self::$tracking_key_to_set = $tracking_key;
91
+ return true;
92
+ }
93
+
94
+ /**
95
+ * Get current session key
96
+ *
97
+ * @return string|false
98
+ */
99
+ public static function get_current_tracking_key() {
100
+ if ( ! self::session_tracking_enabled() ) {
101
+ return false;
102
+ }
103
+
104
+ // If a new tracking key will be set in the request, use that in favour of current cookie value
105
+ if ( self::$tracking_key_to_set && ! headers_sent() ) {
106
+ return self::$tracking_key_to_set;
107
+ }
108
+
109
+ return self::get_tracking_cookie();
110
+ }
111
+
112
+
113
+ /**
114
+ * Returns the current user ID factoring in any session cookies.
115
+ *
116
+ * @return int
117
+ */
118
+ public static function get_detected_user_id() {
119
+
120
+ if ( is_user_logged_in() ) {
121
+ return get_current_user_id();
122
+ }
123
+ return 0;
124
+ }
125
+
126
+
127
+ /**
128
+ * Returns the current guest from tracking cookie.
129
+ *
130
+ * @return Guest|bool
131
+ */
132
+ public static function get_current_guest() {
133
+ if ( ! self::session_tracking_enabled() ) {
134
+ return false;
135
+ }
136
+
137
+ if ( is_user_logged_in() ) {
138
+ return false;
139
+ }
140
+
141
+ global $woocommerce;
142
+
143
+ //Can't look up the guest in this situation.
144
+ if ( ! isset( $woocommerce->session ) ) {
145
+ return false;
146
+ }
147
+
148
+ $tracking_key = $woocommerce->session->get_customer_id();
149
+ if ( $tracking_key ) {
150
+ $guest = IG_ES_Guest_Factory::get_by_key( $tracking_key );
151
+ return $guest;
152
+ }
153
+
154
+ return false;
155
+ }
156
+
157
+ /**
158
+ * Updates the current session based on the customer's email.
159
+ *
160
+ * Create the customer for the email if needed and contains logic to handle when a customers email changes.
161
+ *
162
+ * Cases to handle:
163
+ *
164
+ * - Registered user is logged in or remembered via cookie = bail
165
+ * - Email matches existing customer
166
+ * - Cookie customer exists
167
+ * - Cookie and matched customer are the same = do nothing
168
+ * - Cookie and matched customer are different = cookie must be changed, clear cart from previous key to avoid duplicates
169
+ * - No cookie customer = Set new cookie to matched customer key
170
+ * - Email is new
171
+ * - Cookie customer exists
172
+ * - Customer data is locked = create new customer, change cookie, clear cart from previous key to avoid duplicates
173
+ * - Customer data is not locked = update customer email
174
+ * - No cookie customer = Set new cookie to matched customer key
175
+ *
176
+ * @param string $new_email
177
+ * @param string $language
178
+ *
179
+ * @return Customer|false
180
+ */
181
+ public static function set_session_by_captured_email( $new_email, $language = '' ) {
182
+
183
+ if ( ! is_email( $new_email ) || headers_sent() || ! self::session_tracking_enabled() ) {
184
+ // must have a valid email, be able to set cookies, have session tracking enabled
185
+ return false;
186
+ }
187
+
188
+ $new_email = ES_Clean::email( $new_email );
189
+ $existing_session_customer = self::get_session_customer(); // existing session customer from cookie
190
+ $customer_matching_email = IG_ES_Customer_Factory::get_by_email( $new_email, false ); // important! don't create new customer
191
+ $email_is_new = false === $customer_matching_email;
192
+
193
+ if ( $existing_session_customer && $existing_session_customer->is_registered() ) {
194
+ return $existing_session_customer; // bail if a registered user is already being tracked
195
+ }
196
+
197
+ // Check if a customer already exists matching the supplied email
198
+ if ( $customer_matching_email ) {
199
+
200
+ if ( ! ( $existing_session_customer && $new_email === $existing_session_customer->get_email() ) ) {
201
+ // Customer has changed so delete the cart for the existing customer
202
+ // To avoid duplicate abandoned cart emails
203
+ if ( $existing_session_customer ) {
204
+ $existing_session_customer->delete_cart();
205
+ }
206
+ }
207
+
208
+ // Set the matched customer as the new customer
209
+ $new_customer = $customer_matching_email;
210
+ } else {
211
+ // Is there an existing session customer
212
+ if ( $existing_session_customer ) {
213
+ // Check if existing and new emails are the same
214
+ // This is actually impossible considering the previous logic but it's probably more confusing to omit this
215
+ if ( $existing_session_customer->get_email() === $new_email ) {
216
+ // Nothing to do
217
+ $new_customer = $existing_session_customer;
218
+ } else {
219
+ $guest = $existing_session_customer->get_guest(); // customer can not be a registered user at this point
220
+
221
+ if ( $guest->is_locked() ) {
222
+ // email has changed and guest is locked so we must create a new guest
223
+ // first clear the old guests cart, to avoid duplicate abandoned cart emails
224
+ $guest->delete_cart();
225
+ $new_customer = IG_ES_Customer_Factory::get_by_email( $new_email );
226
+ } else {
227
+ // Guest is not locked so we can simply update guest email
228
+ $guest->set_email( $new_email );
229
+ $guest->save();
230
+
231
+ // Set the new customer to the existing session customer
232
+ $new_customer = $existing_session_customer;
233
+ }
234
+ }
235
+ } else {
236
+ // There is no session customer, so create one
237
+ $new_customer = IG_ES_Customer_Factory::get_by_email( $new_email );
238
+ }
239
+ }
240
+
241
+ // init the new customer tracking, also saves/updates the language
242
+ // if ( $new_customer ) {
243
+ // self::set_session_customer( $new_customer, $language );
244
+ // }
245
+
246
+ // update the stored cart
247
+ if ( IG_ES_Abandoned_Cart_Options::is_cart_tracking_enabled() ) {
248
+ IG_ES_WC_Carts::update_stored_customer_cart( $new_customer );
249
+ }
250
+
251
+ return $new_customer;
252
+ }
253
+
254
+ /**
255
+ * Returns the current session customer and takes into account session tracking cookies.
256
+ *
257
+ * @return Customer|false
258
+ */
259
+ public static function get_session_customer() {
260
+
261
+ global $woocommerce;
262
+
263
+ if ( is_user_logged_in() ) {
264
+ return ig_es_get_logged_in_customer();
265
+ }
266
+
267
+ if ( ! self::session_tracking_enabled() ) {
268
+ return false;
269
+ }
270
+
271
+ //Can't look up the customer in this situation.
272
+ if ( ! isset( $woocommerce->session ) ) {
273
+ return '';
274
+ }
275
+
276
+ $tracking_key = $woocommerce->session->get_customer_id();
277
+
278
+ // uses the newly set key if it exists and can be set
279
+ if ( $tracking_key ) {
280
+ $guest = IG_ES_Guest_Factory::get_by_key( $tracking_key );
281
+ if ( $guest instanceof IG_ES_Guest ) {
282
+ $customer = new IG_ES_Customer();
283
+ $customer->set_prop( 'guest_id', $guest->get_id() );
284
+ $customer->exists = true;
285
+ return $customer;
286
+ }
287
+ }
288
+
289
+ return false;
290
+ }
291
+
292
+ /**
293
+ * Check if we can track user session
294
+ */
295
+ public static function session_tracking_enabled() {
296
+
297
+ if ( isset( $_COOKIE['ig_es_session_tracking_disabled'] ) ) {
298
+ return false;
299
+ }
300
+
301
+ return true;
302
+ }
303
+
304
+ }
lite/includes/classes/ig-es-wc-cookies.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! defined( 'ABSPATH' ) ) {
4
+ exit;
5
+ }
6
+
7
+ /**
8
+ * Class IG_ES_WC_Cookies
9
+ * @since 4.6.5
10
+ */
11
+ class IG_ES_WC_Cookies {
12
+
13
+
14
+ /**
15
+ * Sets a cookie and also updates the $_COOKIE array.
16
+ *
17
+ * @param string $name
18
+ * @param string $value
19
+ * @param int $expire timestamp
20
+ *
21
+ * @return bool
22
+ */
23
+ public static function set( $name, $value, $expire = 0 ) {
24
+ wc_setcookie( $name, $value, $expire );
25
+ $_COOKIE[ $name ] = $value;
26
+ return true;
27
+ }
28
+
29
+
30
+ /**
31
+ * Gets a cookie
32
+ *
33
+ * @param $name
34
+ * @return mixed
35
+ */
36
+ public static function get( $name ) {
37
+ return isset( $_COOKIE[ $name ] ) ? sanitize_title( sanitize_text_field( $_COOKIE[ $name ] ) ) : false;
38
+ }
39
+
40
+
41
+ /**
42
+ * Clear a cookie and also updates the $_COOKIE array.
43
+ * @param $name
44
+ */
45
+ public static function clear( $name ) {
46
+ if ( isset( $_COOKIE[ $name ] ) ) {
47
+ wc_setcookie( $name, '', time() - HOUR_IN_SECONDS );
48
+ unset( $_COOKIE[ $name ] );
49
+ }
50
+ }
51
+
52
+ }
lite/includes/db/class-es-db-actions.php CHANGED
@@ -405,4 +405,37 @@ class ES_DB_Actions extends ES_DB {
405
 
406
  return $count;
407
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
  }
405
 
406
  return $count;
407
  }
408
+
409
+ /**
410
+ * Get Last opened at based on contact_ids
411
+ *
412
+ * @param array $contact_ids
413
+ *
414
+ * @return array
415
+ *
416
+ * @since 4.6.5
417
+ */
418
+ public function get_last_opened_of_contact_ids( $contact_ids = '', $filter = false ) {
419
+
420
+ global $wpdb;
421
+
422
+ if ( empty( $contact_ids ) ) {
423
+ return array();
424
+ }
425
+
426
+ $contact_ids_str = implode( ',', $contact_ids );
427
+
428
+ $result = $wpdb->get_results( $wpdb->prepare( "SELECT contact_id, MAX(created_at) as last_opened_at FROM {$wpdb->prefix}ig_actions WHERE FIND_IN_SET( contact_id, %s ) AND type = %d GROUP BY contact_id", $contact_ids_str, IG_MESSAGE_OPEN ), ARRAY_A );
429
+
430
+ if ( $filter ) {
431
+ $last_opened_at = array_column($result, 'last_opened_at', 'contact_id');
432
+ foreach ( $last_opened_at as $contact_id => $timestamp ) {
433
+ $convert_date_format = get_option( 'date_format' );
434
+ $convert_time_format = get_option( 'time_format' );
435
+ $last_opened_at[ $contact_id ] = get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $timestamp ), $convert_date_format . ' ' . $convert_time_format );
436
+ }
437
+ return $last_opened_at;
438
+ }
439
+ return $result;
440
+ }
441
  }
lite/includes/db/class-es-db-contacts.php CHANGED
@@ -295,18 +295,20 @@ class ES_DB_Contacts extends ES_DB {
295
  return array();
296
  }
297
 
298
- global $wpdb;
299
 
300
  // Check if we have got array of list ids.
301
  if ( is_array( $list_id ) ) {
302
- $list_ids_str = implode( ',', $list_id );
303
- $where = $wpdb->prepare(
304
- "id IN (SELECT contact_id FROM {$wpdb->prefix}ig_lists_contacts WHERE FIND_IN_SET( list_id, %s ) AND status IN ('subscribed', 'confirmed')) AND id NOT IN(SELECT contact_id FROM {$wpdb->prefix}ig_sending_queue WHERE mailing_queue_id = %d )",
305
- $list_ids_str,
306
- $mailing_queue_id
 
 
307
  );
308
  } else {
309
- $where = $wpdb->prepare( "id IN (SELECT contact_id FROM {$wpdb->prefix}ig_lists_contacts WHERE list_id = %d AND status IN ('subscribed', 'confirmed')) AND id NOT IN(SELECT contact_id FROM {$wpdb->prefix}ig_sending_queue WHERE mailing_queue_id = %d )", $list_id, $mailing_queue_id );
310
  }
311
 
312
  return $this->get_by_conditions( $where );
@@ -946,8 +948,14 @@ class ES_DB_Contacts extends ES_DB {
946
  */
947
  public function insert( $data, $type = '' ) {
948
  $source = array( 'admin','import' );
949
- if ( empty( $data['ip_address'] ) && ! in_array( $data['source'], $source ) ) {
950
- $data['ip_address'] = ig_es_get_ip();
 
 
 
 
 
 
951
  }
952
  return parent::insert( $data, $type );
953
  }
295
  return array();
296
  }
297
 
298
+ global $wpbd;
299
 
300
  // Check if we have got array of list ids.
301
  if ( is_array( $list_id ) ) {
302
+ $ids_count = count( $list_id );
303
+ $ids_placeholders = array_fill( 0, $ids_count, '%d' );
304
+ $query_args = $list_id;
305
+ $query_args[] = $mailing_queue_id;
306
+ $where = $wpbd->prepare(
307
+ "id IN (SELECT contact_id FROM {$wpbd->prefix}ig_lists_contacts WHERE list_id IN( " . implode( ',', $ids_placeholders ) . " ) AND status IN ('subscribed', 'confirmed')) AND id NOT IN(SELECT contact_id FROM {$wpbd->prefix}ig_sending_queue WHERE mailing_queue_id = %d )",
308
+ $query_args
309
  );
310
  } else {
311
+ $where = $wpbd->prepare( "id IN (SELECT contact_id FROM {$wpbd->prefix}ig_lists_contacts WHERE list_id = %d AND status IN ('subscribed', 'confirmed')) AND id NOT IN(SELECT contact_id FROM {$wpbd->prefix}ig_sending_queue WHERE mailing_queue_id = %d )", $list_id, $mailing_queue_id );
312
  }
313
 
314
  return $this->get_by_conditions( $where );
948
  */
949
  public function insert( $data, $type = '' ) {
950
  $source = array( 'admin','import' );
951
+ $can_track_ip = apply_filters('ig_es_can_track_subscriber_ip', 'yes' );
952
+
953
+ if ( 'no' === $can_track_ip && ES()->is_pro() ) {
954
+ $data['ip_address'] = '';
955
+ } else {
956
+ if ( empty( $data['ip_address'] ) && ! in_array( $data['source'], $source ) ) {
957
+ $data['ip_address'] = ig_es_get_ip();
958
+ }
959
  }
960
  return parent::insert( $data, $type );
961
  }
lite/includes/db/class-es-db-lists-contacts.php CHANGED
@@ -107,6 +107,11 @@ class ES_DB_Lists_Contacts extends ES_DB {
107
 
108
  if ( is_array( $list_ids ) && count( $list_ids ) > 0 ) {
109
 
 
 
 
 
 
110
  // Remove entry if it's already there in a list
111
  $contact_id = ! empty( $contact_data['contact_id'] ) ? $contact_data['contact_id'] : 0;
112
  $this->remove_contacts_from_lists( $contact_id, $list_ids );
107
 
108
  if ( is_array( $list_ids ) && count( $list_ids ) > 0 ) {
109
 
110
+ $can_track_ip = apply_filters( 'ig_es_can_track_subscriber_ip', 'yes' );
111
+ if ( 'no' === $can_track_ip && ES()->is_pro() ) {
112
+ $contact_data['subscribed_ip'] = '';
113
+ }
114
+
115
  // Remove entry if it's already there in a list
116
  $contact_id = ! empty( $contact_data['contact_id'] ) ? $contact_data['contact_id'] : 0;
117
  $this->remove_contacts_from_lists( $contact_id, $list_ids );
lite/includes/db/class-ig-es-db-wc-cart.php ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Workflow Queue DB
4
+ *
5
+ * @since 4.4.1
6
+ * @version 1.0
7
+ * @package Email Subscribers
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * IG_ES_DB_WC_Cart class
17
+ *
18
+ *@since 4.6.5
19
+ */
20
+ class IG_ES_DB_WC_Cart extends ES_DB {
21
+
22
+ /**
23
+ * Workflow queue table name
24
+ *
25
+ *@since 4.6.5
26
+ * @var $table_name
27
+ */
28
+ public $table_name;
29
+
30
+ /**
31
+ * Workflow queue table version
32
+ *
33
+ *@since 4.6.5
34
+ * @var $version
35
+ */
36
+ public $version;
37
+
38
+ /**
39
+ * Workflow queue table primary key
40
+ *
41
+ *@since 4.6.5
42
+ * @var $primary_key
43
+ */
44
+ public $primary_key;
45
+
46
+ /**
47
+ * IG_ES_DB_WC_Cart constructor.
48
+ *
49
+ *@since 4.6.5
50
+ */
51
+ public function __construct() {
52
+ global $wpdb;
53
+
54
+ parent::__construct();
55
+
56
+ $this->table_name = $wpdb->prefix . 'ig_wc_cart';
57
+ $this->primary_key = 'id';
58
+
59
+ $this->version = '1.0';
60
+ }
61
+
62
+ /**
63
+ * Returns workflow queue table's columns
64
+ *
65
+ *@since 4.6.5
66
+ *
67
+ * @return array workflow queue table columns
68
+ */
69
+ public function get_columns() {
70
+ return array(
71
+ 'id' => '%d',
72
+ 'status' => '%s',
73
+ 'user_id' => '%d',
74
+ 'guest_id' => '%d',
75
+ 'last_modified' => '%s',
76
+ 'created' => '%s',
77
+ 'items' => '%s',
78
+ 'coupons' => '%s',
79
+ 'fees' => '%s',
80
+ 'shipping_tax_total' => '%f',
81
+ 'shipping_total' => '%f',
82
+ 'total' => '%f',
83
+ 'token' => '%s',
84
+ 'currency' => '%s',
85
+ );
86
+ }
87
+
88
+ /**
89
+ * Returns default values for workflow columns
90
+ *
91
+ *@since 4.6.5
92
+ *
93
+ * @return array default values for workflow columns
94
+ */
95
+ public function get_column_defaults() {
96
+ return array(
97
+ 'id' => 0,
98
+ 'status' => '',
99
+ 'user_id' => 0,
100
+ 'guest_id' => 0,
101
+ 'last_modified' => '',
102
+ 'created' => '',
103
+ 'items' => '',
104
+ 'coupons' => '',
105
+ 'fees' => '',
106
+ 'shipping_tax_total' => 0,
107
+ 'shipping_total' => 0,
108
+ 'total' => 0,
109
+ 'token' => '',
110
+ 'currency' => '',
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Get workflows based on arguements
116
+ *
117
+ * @param array $query_args Query arguements.
118
+ * @param string $output Output format.
119
+ * @param boolean $do_count_only Count only flag.
120
+ *
121
+ * @return mixed $result Query result
122
+ *
123
+ *@since 4.6.5
124
+ */
125
+ public function get_carts( $query_args = array(), $output = ARRAY_A, $do_count_only = false ) {
126
+
127
+ global $wpdb, $wpbd;
128
+ if ( $do_count_only ) {
129
+ $sql = 'SELECT count(*) as total FROM ' . $wpdb->prefix . 'ig_wc_cart';
130
+ } else {
131
+ $sql = 'SELECT ';
132
+ if ( ! empty( $query_args['fields'] ) && is_array( $query_args['fields'] ) ) {
133
+ $sql .= implode( ' ,', $query_args['fields'] );
134
+ } else {
135
+ $sql .= '*';
136
+ }
137
+
138
+ $sql .= ' FROM ' . $wpdb->prefix . 'ig_wc_cart';
139
+ }
140
+
141
+ $args = array();
142
+ $query = array();
143
+
144
+ if ( ! empty( $query_args['ids'] ) ) {
145
+ $ids_count = count( $query_args['ids'] );
146
+ $ids_placeholders = array_fill( 0, $ids_count, '%d' );
147
+ $query[] = ' id IN( ' . implode( ',', $ids_placeholders ) . ' )';
148
+ $args = array_merge( $args, $query_args['ids'] );
149
+ }
150
+
151
+ if ( isset( $query_args['status'] ) ) {
152
+ $query[] = ' status = %s ';
153
+ $args[] = $query_args['status'];
154
+ }
155
+
156
+ if ( isset( $query_args['last_modified'] ) ) {
157
+ $query[] = ' last_modified <= %s ';
158
+ $args[] = $query_args['last_modified'];
159
+ }
160
+
161
+ $query = apply_filters( 'ig_es_wc_cart_where_caluse', $query );
162
+
163
+ if ( count( $query ) > 0 ) {
164
+ $sql .= ' WHERE ';
165
+
166
+ $sql .= implode( ' AND ', $query );
167
+
168
+ if ( count( $args ) > 0 ) {
169
+ $sql = $wpbd->prepare( $sql, $args ); // phpcs:ignore
170
+ }
171
+ }
172
+
173
+ if ( ! $do_count_only ) {
174
+
175
+ $order = ! empty( $query_args['order'] ) ? strtolower( $query_args['order'] ) : 'desc';
176
+ $expected_order_values = array( 'asc', 'desc' );
177
+ if ( ! in_array( $order, $expected_order_values, true ) ) {
178
+ $order = 'desc';
179
+ }
180
+
181
+ $default_order_by = esc_sql( 'created' );
182
+
183
+ $expected_order_by_values = array( 'created' );
184
+ if ( empty( $query_args['order_by'] ) || ! in_array( $query_args['order_by'], $expected_order_by_values, true ) ) {
185
+ $order_by_clause = " ORDER BY {$default_order_by} DESC";
186
+ } else {
187
+ $order_by = esc_sql( $query_args['order_by'] );
188
+ $order_by_clause = " ORDER BY {$order_by} {$order}, {$default_order_by} DESC";
189
+ }
190
+
191
+ $sql .= $order_by_clause;
192
+
193
+ if ( ! empty( $query_args['per_page'] ) ) {
194
+ $sql .= ' LIMIT ' . $query_args['per_page'];
195
+ if ( ! empty( $query_args['page_number'] ) ) {
196
+ $sql .= ' OFFSET ' . ( $query_args['page_number'] - 1 ) * $query_args['per_page'];
197
+ }
198
+ }
199
+
200
+ $result = $wpbd->get_results( $sql, $output ); // phpcs:ignore
201
+ } else {
202
+ $result = $wpbd->get_var( $sql ); // phpcs:ignore
203
+ }
204
+
205
+ return $result;
206
+
207
+ }
208
+ }
lite/includes/db/class-ig-es-db-wc-guest.php ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Guest Queue DB
4
+ *
5
+ * @since 4.4.1
6
+ * @version 1.0
7
+ * @package Email Subscribers
8
+ */
9
+
10
+ // Exit if accessed directly.
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ /**
16
+ * IG_ES_DB_WC_Guest class
17
+ *
18
+ *@since 4.6.5
19
+ */
20
+ class IG_ES_DB_WC_Guest extends ES_DB {
21
+
22
+ /**
23
+ * Guest queue table name
24
+ *
25
+ *@since 4.6.5
26
+ * @var $table_name
27
+ */
28
+ public $table_name;
29
+
30
+ /**
31
+ * Guest queue table version
32
+ *
33
+ *@since 4.6.5
34
+ * @var $version
35
+ */
36
+ public $version;
37
+
38
+ /**
39
+ * Guest queue table primary key
40
+ *
41
+ *@since 4.6.5
42
+ * @var $primary_key
43
+ */
44
+ public $primary_key;
45
+
46
+ /**
47
+ * IG_ES_DB_WC_Guest constructor.
48
+ *
49
+ *@since 4.6.5
50
+ */
51
+ public function __construct() {
52
+ global $wpdb;
53
+
54
+ parent::__construct();
55
+
56
+ $this->table_name = $wpdb->prefix . 'ig_wc_guests';
57
+ $this->primary_key = 'id';
58
+
59
+ $this->version = '1.0';
60
+ }
61
+
62
+ /**
63
+ * Returns Guest queue table's columns
64
+ *
65
+ *@since 4.6.5
66
+ *
67
+ * @return array Guest queue table columns
68
+ */
69
+ public function get_columns() {
70
+ return array(
71
+ 'id' => '%d',
72
+ 'email' => '%s',
73
+ 'tracking_key' => '%s',
74
+ 'created' => '%s',
75
+ 'last_active' => '%s',
76
+ 'language' => '%s',
77
+ 'most_recent_order' => '%d',
78
+ 'version' => '%d',
79
+ );
80
+ }
81
+
82
+ /**
83
+ * Returns default values for Guest columns
84
+ *
85
+ *@since 4.6.5
86
+ *
87
+ * @return array default values for Guest columns
88
+ */
89
+ public function get_column_defaults() {
90
+ return array(
91
+ 'id' => 0,
92
+ 'email' => '',
93
+ 'tracking_key' => '',
94
+ 'created' => '',
95
+ 'last_active' => '',
96
+ 'language' => '',
97
+ 'most_recent_order' => 0,
98
+ 'version' => 0,
99
+ );
100
+ }
101
+
102
+ /**
103
+ * Get Guests based on arguements
104
+ *
105
+ * @param array $query_args Query arguements.
106
+ * @param string $output Output format.
107
+ * @param boolean $do_count_only Count only flag.
108
+ *
109
+ * @return mixed $result Query result
110
+ *
111
+ *@since 4.6.5
112
+ */
113
+ public function get_carts( $query_args = array(), $output = ARRAY_A, $do_count_only = false ) {
114
+
115
+ global $wpdb, $wpbd;
116
+ if ( $do_count_only ) {
117
+ $sql = 'SELECT count(*) as total FROM ' . $wpdb->prefix . 'ig_wc_cart';
118
+ } else {
119
+ $sql = 'SELECT ';
120
+ if ( ! empty( $query_args['fields'] ) && is_array( $query_args['fields'] ) ) {
121
+ $sql .= implode( ' ,', $query_args['fields'] );
122
+ } else {
123
+ $sql .= '*';
124
+ }
125
+
126
+ $sql .= ' FROM ' . $wpdb->prefix . 'ig_wc_cart';
127
+ }
128
+
129
+ $args = array();
130
+ $query = array();
131
+
132
+ if ( ! empty( $query_args['ids'] ) ) {
133
+ $ids_count = count( $query_args['ids'] );
134
+ $ids_placeholders = array_fill( 0, $ids_count, '%d' );
135
+ $query[] = ' id IN( ' . implode( ',', $ids_placeholders ) . ' )';
136
+ $args = array_merge( $args, $query_args['ids'] );
137
+ }
138
+
139
+ if ( isset( $query_args['status'] ) ) {
140
+ $query[] = ' status = %s ';
141
+ $args[] = $query_args['status'];
142
+ }
143
+
144
+ if ( isset( $query_args['last_modified'] ) ) {
145
+ $query[] = ' last_modified <= %s ';
146
+ $args[] = $query_args['last_modified'];
147
+ }
148
+
149
+ $query = apply_filters( 'ig_es_wc_cart_where_caluse', $query );
150
+
151
+ if ( count( $query ) > 0 ) {
152
+ $sql .= ' WHERE ';
153
+
154
+ $sql .= implode( ' AND ', $query );
155
+
156
+ if ( count( $args ) > 0 ) {
157
+ $sql = $wpbd->prepare( $sql, $args ); // phpcs:ignore
158
+ }
159
+ }
160
+
161
+ if ( ! $do_count_only ) {
162
+
163
+ $order = ! empty( $query_args['order'] ) ? strtolower( $query_args['order'] ) : 'desc';
164
+ $expected_order_values = array( 'asc', 'desc' );
165
+ if ( ! in_array( $order, $expected_order_values, true ) ) {
166
+ $order = 'desc';
167
+ }
168
+
169
+ $default_order_by = esc_sql( 'created' );
170
+
171
+ $expected_order_by_values = array( 'created' );
172
+ if ( empty( $query_args['order_by'] ) || ! in_array( $query_args['order_by'], $expected_order_by_values, true ) ) {
173
+ $order_by_clause = " ORDER BY {$default_order_by} DESC";
174
+ } else {
175
+ $order_by = esc_sql( $query_args['order_by'] );
176
+ $order_by_clause = " ORDER BY {$order_by} {$order}, {$default_order_by} DESC";
177
+ }
178
+
179
+ $sql .= $order_by_clause;
180
+
181
+ if ( ! empty( $query_args['per_page'] ) ) {
182
+ $sql .= ' LIMIT ' . $query_args['per_page'];
183
+ if ( ! empty( $query_args['page_number'] ) ) {
184
+ $sql .= ' OFFSET ' . ( $query_args['page_number'] - 1 ) * $query_args['per_page'];
185
+ }
186
+ }
187
+
188
+ $result = $wpbd->get_results( $sql, $output ); // phpcs:ignore
189
+ } else {
190
+ $result = $wpbd->get_var( $sql ); // phpcs:ignore
191
+ }
192
+
193
+ return $result;
194
+
195
+ }
196
+ }
lite/includes/es-core-functions.php CHANGED
@@ -978,3 +978,23 @@ if ( ! function_exists( 'ig_es_insert_widget_in_sidebar' ) ) {
978
  return false;
979
  }
980
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
978
  return false;
979
  }
980
  }
981
+
982
+ /**
983
+ * Method to increase response size while making http request.
984
+ *
985
+ * @param array $args Request arguements.
986
+ *
987
+ * @return array $args Request arguements.
988
+ *
989
+ * @since 4.6.5
990
+ */
991
+ if ( ! function_exists( 'ig_es_increase_http_response_size') ) {
992
+
993
+ function ig_es_increase_http_response_size( $args = array() ) {
994
+
995
+ // Increase the reponse size to 1500 KB.
996
+ $args['limit_response_size'] = 1536000; // 1500 KB
997
+
998
+ return $args;
999
+ }
1000
+ }
lite/includes/pro-features.php CHANGED
@@ -683,7 +683,7 @@ function add_spam_score_utm_link() {
683
  */
684
  function ig_es_add_captcha_option( $form_data ) {
685
 
686
- if ( ES()->can_upsell_features( array( 'lite', 'starter', 'trial' ) ) ) {
687
 
688
  $utm_args = array(
689
  'utm_medium' => 'es_form_captcha'
683
  */
684
  function ig_es_add_captcha_option( $form_data ) {
685
 
686
+ if ( ES()->can_upsell_features( array( 'lite', 'trial' ) ) ) {
687
 
688
  $utm_args = array(
689
  'utm_medium' => 'es_form_captcha'
lite/includes/upgrade/es-update-functions.php CHANGED
@@ -1306,3 +1306,23 @@ function ig_es_update_463_db_version() {
1306
  ES_Install::update_db_version( '4.6.3' );
1307
  }
1308
  /* --------------------- ES 4.6.3(End)--------------------------- */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1306
  ES_Install::update_db_version( '4.6.3' );
1307
  }
1308
  /* --------------------- ES 4.6.3(End)--------------------------- */
1309
+
1310
+ /* --------------------- ES 4.6.5(Start)--------------------------- */
1311
+ /**
1312
+ * Create Abandoned Carts Tables.
1313
+ *
1314
+ * @since 4.6.5
1315
+ */
1316
+ function ig_es_update_465_create_tables() {
1317
+ ES_Install::create_tables( '4.6.5' );
1318
+ }
1319
+
1320
+ /**
1321
+ * Update DB version
1322
+ *
1323
+ * @since 4.6.5
1324
+ */
1325
+ function ig_es_update_465_db_version() {
1326
+ ES_Install::update_db_version( '4.6.5' );
1327
+ }
1328
+ /* --------------------- ES 4.6.5(End)--------------------------- */
lite/includes/workflows/admin/class-es-workflow-admin-edit.php CHANGED
@@ -337,7 +337,9 @@ class ES_Workflow_Admin_Edit {
337
  $action_select_box_values = array();
338
 
339
  foreach ( ES_Workflow_Actions::get_all() as $action ) {
340
- $action_select_box_values[ $action->get_group() ][ $action->get_name() ] = $action->get_title();
 
 
341
  }
342
 
343
  ES_Workflow_Admin::get_view(
@@ -469,6 +471,10 @@ class ES_Workflow_Admin_Edit {
469
  }
470
  }
471
 
 
 
 
 
472
  return $workflow_id;
473
  }
474
 
337
  $action_select_box_values = array();
338
 
339
  foreach ( ES_Workflow_Actions::get_all() as $action ) {
340
+ if ( $action instanceof ES_Workflow_Action ) {
341
+ $action_select_box_values[ $action->get_group() ][ $action->get_name() ] = $action->get_title();
342
+ }
343
  }
344
 
345
  ES_Workflow_Admin::get_view(
471
  }
472
  }
473
 
474
+ if ( $workflow_id ) {
475
+ do_action( 'ig_es_workflow_updated', $workflow_id, $workflow_data );
476
+ }
477
+
478
  return $workflow_id;
479
  }
480
 
lite/includes/workflows/admin/views/meta-box-trigger.php CHANGED
@@ -11,7 +11,9 @@
11
  $trigger_list = array();
12
 
13
  foreach ( ES_Workflow_Triggers::get_all() as $trigger ) {
14
- $trigger_list[ $trigger->get_group() ][ $trigger->get_name() ] = $trigger;
 
 
15
  }
16
 
17
  if ( ! ES()->is_starter() ) {
11
  $trigger_list = array();
12
 
13
  foreach ( ES_Workflow_Triggers::get_all() as $trigger ) {
14
+ if ( $trigger instanceof ES_Workflow_Trigger ) {
15
+ $trigger_list[ $trigger->get_group() ][ $trigger->get_name() ] = $trigger;
16
+ }
17
  }
18
 
19
  if ( ! ES()->is_starter() ) {
lite/includes/workflows/admin/views/trigger-fields.php CHANGED
@@ -3,8 +3,8 @@
3
  /**
4
  * Can be loaded by ajax
5
  *
6
- * @var $workflow AutomateWoo\Workflow
7
- * @var $trigger AutomateWoo\Trigger
8
  * @var $fill_fields (optional)
9
  */
10
 
3
  /**
4
  * Can be loaded by ajax
5
  *
6
+ * @var $workflow Workflow
7
+ * @var $trigger Trigger
8
  * @var $fill_fields (optional)
9
  */
10
 
lite/includes/workflows/class-es-workflow-data-layer.php CHANGED
@@ -95,4 +95,32 @@ class ES_Workflow_Data_Layer {
95
  return $is_missing;
96
  }
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }
95
  return $is_missing;
96
  }
97
 
98
+ /**
99
+ * Get customer object from data layer
100
+ *
101
+ * @return IG_ES_Customer|false
102
+ */
103
+ public function get_customer() {
104
+ return $this->get_item( 'customer' );
105
+ }
106
+
107
+
108
+ /**
109
+ * Get cart object from data layer
110
+ *
111
+ * @return IG_ES_Cart|false
112
+ */
113
+ public function get_cart() {
114
+ return $this->get_item( 'cart' );
115
+ }
116
+
117
+ /**
118
+ * Get cart object from data layer
119
+ *
120
+ * @return IG_ES_Guest|false
121
+ */
122
+ public function get_guest() {
123
+ return $this->get_item( 'guest' );
124
+ }
125
+
126
  }
lite/includes/workflows/class-es-workflow-query.php CHANGED
@@ -26,6 +26,13 @@ class ES_Workflow_Query {
26
  * @var string|ES_Workflow_Trigger
27
  */
28
  public $trigger;
 
 
 
 
 
 
 
29
 
30
  /**
31
  * Query arguements
@@ -67,6 +74,34 @@ class ES_Workflow_Query {
67
  }
68
  }
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  /**
71
  * Set return object
72
  *
@@ -88,6 +123,10 @@ class ES_Workflow_Query {
88
  $this->args['trigger_name'] = $this->trigger;
89
  }
90
 
 
 
 
 
91
  $this->args['fields'] = array();
92
  if ( 'ids' === $this->return ) {
93
  $this->args['fields'][] = 'id';
26
  * @var string|ES_Workflow_Trigger
27
  */
28
  public $trigger;
29
+
30
+ /**
31
+ * Trigger names
32
+ *
33
+ * @var array|ES_Workflow_Trigger
34
+ */
35
+ public $triggers;
36
 
37
  /**
38
  * Query arguements
74
  }
75
  }
76
 
77
+ /**
78
+ * Set trigger name or array of names to query.
79
+ *
80
+ * @param string|ES_Workflow_Trigger $trigger Workflow trigger object|name.
81
+ */
82
+ public function set_triggers( $triggers ) {
83
+ if ( ! empty( $triggers ) ) {
84
+ foreach ( $triggers as $trigger ) {
85
+ if ( $trigger instanceof ES_Workflow_Trigger ) {
86
+ $this->triggers[] = $trigger->get_name();
87
+ } else {
88
+ $this->triggers[] = $trigger;
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Get workflows by status
96
+ *
97
+ * @since 4.6.5
98
+ * @param int $status
99
+ * @return $this
100
+ */
101
+ public function where_status( $status ) {
102
+ $this->args['status'] = $status;
103
+ }
104
+
105
  /**
106
  * Set return object
107
  *
123
  $this->args['trigger_name'] = $this->trigger;
124
  }
125
 
126
+ if ( $this->triggers ) {
127
+ $this->args['trigger_names'] = $this->triggers;
128
+ }
129
+
130
  $this->args['fields'] = array();
131
  if ( 'ids' === $this->return ) {
132
  $this->args['fields'][] = 'id';
lite/includes/workflows/class-es-workflow.php CHANGED
@@ -392,7 +392,6 @@ class ES_Workflow {
392
  return false;
393
  }
394
 
395
-
396
  if ( ! $trigger->validate_workflow( $this ) ) {
397
  return false;
398
  }
392
  return false;
393
  }
394
 
 
395
  if ( ! $trigger->validate_workflow( $this ) ) {
396
  return false;
397
  }
lite/includes/workflows/db/class-es-db-workflows.php CHANGED
@@ -153,6 +153,13 @@ class ES_DB_Workflows extends ES_DB {
153
  $args[] = $query_args['trigger_name'];
154
  }
155
 
 
 
 
 
 
 
 
156
  if ( isset( $query_args['status'] ) ) {
157
  $query[] = ' status = %d ';
158
  $args[] = $query_args['status'];
@@ -346,6 +353,8 @@ class ES_DB_Workflows extends ES_DB {
346
  $updated = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}ig_workflows SET status = %d WHERE FIND_IN_SET(id, %s)", $status, $workflow_ids_str ) );
347
  }
348
 
 
 
349
  return $updated;
350
 
351
  }
153
  $args[] = $query_args['trigger_name'];
154
  }
155
 
156
+ if ( ! empty( $query_args['trigger_names'] ) ) {
157
+ $trigger_names_count = count( $query_args['trigger_names'] );
158
+ $trigger_names_placeholders = array_fill( 0, $trigger_names_count, '%s' );
159
+ $query[] = ' trigger_name IN( ' . implode( ',', $trigger_names_placeholders ) . ' )';
160
+ $args = array_merge( $args, $query_args['trigger_names'] );
161
+ }
162
+
163
  if ( isset( $query_args['status'] ) ) {
164
  $query[] = ' status = %d ';
165
  $args[] = $query_args['status'];
353
  $updated = $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}ig_workflows SET status = %d WHERE FIND_IN_SET(id, %s)", $status, $workflow_ids_str ) );
354
  }
355
 
356
+ do_action( 'ig_es_workflow_status_changed', $workflow_ids );
357
+
358
  return $updated;
359
 
360
  }
lite/includes/workflows/queue/class-es-workflow-queue.php CHANGED
@@ -356,24 +356,24 @@ class ES_Workflow_Queue extends ES_DB_Workflows_Queue {
356
  if ( ! $this->exists ) {
357
  return false;
358
  }
359
-
360
  // mark as failed and then delete if complete, so fatal error will not cause it to run repeatedly
361
  $this->mark_as_failed( self::F_FATAL_ERROR );
362
  $this->save();
363
  $success = false;
364
-
365
  $workflow = $this->get_workflow();
366
  $data_layer = $this->get_data_layer();
367
  $workflow->setup( $data_layer );
368
-
369
  $failure = $this->do_failure_check( $workflow );
370
-
371
  if ( $failure ) {
372
  // queued event failed
373
  $this->mark_as_failed( $failure );
374
  } else {
375
  $success = true;
376
-
377
  // passed fail check so validate workflow and then delete
378
  if ( $this->validate_workflow( $workflow ) ) {
379
  $workflow->run();
356
  if ( ! $this->exists ) {
357
  return false;
358
  }
359
+
360
  // mark as failed and then delete if complete, so fatal error will not cause it to run repeatedly
361
  $this->mark_as_failed( self::F_FATAL_ERROR );
362
  $this->save();
363
  $success = false;
364
+
365
  $workflow = $this->get_workflow();
366
  $data_layer = $this->get_data_layer();
367
  $workflow->setup( $data_layer );
368
+
369
  $failure = $this->do_failure_check( $workflow );
370
+
371
  if ( $failure ) {
372
  // queued event failed
373
  $this->mark_as_failed( $failure );
374
  } else {
375
  $success = true;
376
+
377
  // passed fail check so validate workflow and then delete
378
  if ( $this->validate_workflow( $workflow ) ) {
379
  $workflow->run();
lite/includes/workflows/workflow-helper-functions.php CHANGED
@@ -56,3 +56,34 @@ function ig_es_validate_data_item( $type, $item ) {
56
  function ig_es_bool_int( $val ) {
57
  return intval( (bool) $val );
58
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  function ig_es_bool_int( $val ) {
57
  return intval( (bool) $val );
58
  }
59
+
60
+ /**
61
+ * Generate tracking key
62
+ *
63
+ * @param $length int
64
+ * @param bool $case_sensitive When false only lowercase letters will be included
65
+ * @param bool $more_numbers
66
+ * @return string
67
+ */
68
+ function ig_es_generate_key( $length = 25, $case_sensitive = true, $more_numbers = false ) {
69
+
70
+ $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
71
+
72
+ if ( $case_sensitive ) {
73
+ $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
74
+ }
75
+
76
+ if ( $more_numbers ) {
77
+ $chars .= '01234567890123456789';
78
+ }
79
+
80
+ $password = '';
81
+ $chars_length = strlen( $chars );
82
+
83
+ for ( $i = 0; $i < $length; $i++ ) {
84
+ $password .= substr($chars, wp_rand( 0, $chars_length - 1), 1);
85
+ }
86
+
87
+ return $password;
88
+ }
89
+
lite/public/js/email-subscribers-public.js CHANGED
@@ -132,6 +132,7 @@
132
  });
133
  }
134
  });
 
135
  })(jQuery);
136
 
137
 
132
  });
133
  }
134
  });
135
+
136
  })(jQuery);
137
 
138
 
readme.txt CHANGED
@@ -4,9 +4,9 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_i
4
  Author URI: https://www.icegram.com/
5
  Tags: email marketing, subscription, autoresponder, post notification, welcome email
6
  Requires at least: 3.9
7
- Tested up to: 5.5.3
8
  Requires PHP: 5.6
9
- Stable tag: 4.6.4
10
  License: GPLv3
11
  License URI: http://www.gnu.org/licenses
12
 
@@ -304,6 +304,13 @@ Refer [here](https://www.icegram.com/documentation/es-faq/).
304
 
305
  == Changelog ==
306
 
 
 
 
 
 
 
 
307
  **4.6.4 (24.11.2020)**
308
 
309
  * New: Added {{POSTMORETAG}} keyword for Post Notification
4
  Author URI: https://www.icegram.com/
5
  Tags: email marketing, subscription, autoresponder, post notification, welcome email
6
  Requires at least: 3.9
7
+ Tested up to: 5.6
8
  Requires PHP: 5.6
9
+ Stable tag: 4.6.5
10
  License: GPLv3
11
  License URI: http://www.gnu.org/licenses
12
 
304
 
305
  == Changelog ==
306
 
307
+ **4.6.5 (22.12.2020)**
308
+
309
+ * New: WooCommerce Abandoned Cart [PRO]
310
+ * New: Added option to disable storing of subscribers' IP Address [PRO]
311
+ * New: Added new column 'Last Opened At' on the audience page [PRO]
312
+ * New: Added option to filter reports by campaign type and status
313
+
314
  **4.6.4 (24.11.2020)**
315
 
316
  * New: Added {{POSTMORETAG}} keyword for Post Notification