HubSpot – Free Marketing Plugin for WordPress - Version 0.8.3

Version Description

(2014.05.06) = - Bug fixes - Merge duplicate contacts into one record - Remove url parameters from source links in contact list - Downgrade use of singletons so classes are compatabile with PHP 5.2

Download this release

Release Info

Developer AndyGCook
Plugin Icon 128x128 HubSpot – Free Marketing Plugin for WordPress
Version 0.8.3
Comparing to
See all releases

Code changes from version 0.7.0 to 0.8.3

Files changed (162) hide show
  1. admin/inc/class-leadin-contact.php +4 -2
  2. admin/inc/class-leadin-list-table.php +31 -27
  3. admin/js/leadin-admin.js +1 -1
  4. admin/leadin-admin.php +26 -10
  5. admin/sass/.sass-cache/068a0951e0f44f5f7f1ab84833b671b75a0d1dd1/_clearfix.scssc +0 -0
  6. admin/sass/.sass-cache/068a0951e0f44f5f7f1ab84833b671b75a0d1dd1/_hacks.scssc +0 -0
  7. admin/sass/.sass-cache/0c3aa292b717031700a0344f604e0b99b479fc79/_susy.scssc +0 -0
  8. admin/sass/.sass-cache/0c3aa292b717031700a0344f604e0b99b479fc79/_susyone.scssc +0 -0
  9. admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_container.scssc +0 -0
  10. admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_end.scssc +0 -0
  11. admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_isolate.scssc +0 -0
  12. admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_span.scssc +0 -0
  13. admin/sass/.sass-cache/52467c9ff883780d88c16e980bb8d43c6f3f113f/_susy.scssc +0 -0
  14. admin/sass/.sass-cache/52467c9ff883780d88c16e980bb8d43c6f3f113f/_susyone.scssc +0 -0
  15. admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_background.scssc +0 -0
  16. admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_box-sizing.scssc +0 -0
  17. admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_clearfix.scssc +0 -0
  18. admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_prefix.scssc +0 -0
  19. admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_rem.scssc +0 -0
  20. admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_support.scssc +0 -0
  21. admin/sass/.sass-cache/5cb96857c5f923ac31761ca34fd727781525b482/_math.scssc +0 -0
  22. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_animation.scssc +0 -0
  23. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_appearance.scssc +0 -0
  24. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_background-clip.scssc +0 -0
  25. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_background-origin.scssc +0 -0
  26. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_background-size.scssc +0 -0
  27. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_border-radius.scssc +0 -0
  28. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_box-shadow.scssc +0 -0
  29. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_box-sizing.scssc +0 -0
  30. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_columns.scssc +0 -0
  31. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_filter.scssc +0 -0
  32. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_flexbox.scssc +0 -0
  33. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_font-face.scssc +0 -0
  34. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_hyphenation.scssc +0 -0
  35. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_images.scssc +0 -0
  36. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_inline-block.scssc +0 -0
  37. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_opacity.scssc +0 -0
  38. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_regions.scssc +0 -0
  39. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_text-shadow.scssc +0 -0
  40. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_transform.scssc +0 -0
  41. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_transition.scssc +0 -0
  42. admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_user-interface.scssc +0 -0
  43. admin/sass/.sass-cache/797a4968f0b8e5805efbffb5db3326f524464ef6/_float.scssc +0 -0
  44. admin/sass/.sass-cache/797a4968f0b8e5805efbffb5db3326f524464ef6/_shared.scssc +0 -0
  45. admin/sass/.sass-cache/797a4968f0b8e5805efbffb5db3326f524464ef6/_support.scssc +0 -0
  46. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_contact_detail_page.sassc +0 -0
  47. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_contacts_list_page.sassc +0 -0
  48. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_grid.sassc +0 -0
  49. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_powerups_page.sassc +0 -0
  50. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_settings_page.sassc +0 -0
  51. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_tables.sassc +0 -0
  52. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_variables.sassc +0 -0
  53. admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/leadin-admin.sassc +0 -0
  54. admin/sass/.sass-cache/a1d71d7b609c5e9852c5bfc005fe6aac02f7590f/_css3.scssc +0 -0
  55. admin/sass/.sass-cache/a1d71d7b609c5e9852c5bfc005fe6aac02f7590f/_support.scssc +0 -0
  56. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_appearance.scssc +0 -0
  57. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-clip.scssc +0 -0
  58. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-origin.scssc +0 -0
  59. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-size.scssc +0 -0
  60. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_border-radius.scssc +0 -0
  61. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-shadow.scssc +0 -0
  62. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-sizing.scssc +0 -0
  63. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box.scssc +0 -0
  64. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_columns.scssc +0 -0
  65. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_filter.scssc +0 -0
  66. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_font-face.scssc +0 -0
  67. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_hyphenation.scssc +0 -0
  68. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_images.scssc +0 -0
  69. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_inline-block.scssc +0 -0
  70. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_opacity.scssc +0 -0
  71. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_regions.scssc +0 -0
  72. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_shared.scssc +0 -0
  73. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_text-shadow.scssc +0 -0
  74. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transform.scssc +0 -0
  75. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transition.scssc +0 -0
  76. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_background.scssc +0 -0
  77. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_functions.scssc +0 -0
  78. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_grid.scssc +0 -0
  79. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_isolation.scssc +0 -0
  80. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_margin.scssc +0 -0
  81. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_media.scssc +0 -0
  82. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_padding.scssc +0 -0
  83. admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_settings.scssc +0 -0
  84. admin/sass/.sass-cache/a9f64e5d5dd6c28c118d0281ab7b3986e2729ea7/_grid-background.scssc +0 -0
  85. admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_clearfix.scssc +0 -0
  86. admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_hacks.scssc +0 -0
  87. admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_css3.scssc +0 -0
  88. admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_support.scssc +0 -0
  89. admin/sass/.sass-cache/e4b1f93193ae29c5c808f60975dad0a21092da77/_units.scssc +0 -0
  90. admin/sass/.sass-cache/e4b1f93193ae29c5c808f60975dad0a21092da77/_vertical_rhythm.scssc +0 -0
  91. admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_background.scssc +0 -0
  92. admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_container.scssc +0 -0
  93. admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_direction.scssc +0 -0
  94. admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_margins.scssc +0 -0
  95. admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_output.scssc +0 -0
  96. admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_padding.scssc +0 -0
  97. admin/sass/.sass-cache/eba4af504693c8b2c03a74241844b1c5ab414495/_su.scssc +0 -0
  98. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contact_detail_page.sassc +0 -0
  99. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contacts_list_page.sassc +0 -0
  100. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_settings_page.sassc +0 -0
  101. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_tables.sassc +0 -0
  102. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_variables.sassc +0 -0
  103. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/leadin-admin.sassc +0 -0
  104. admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_grid.scssc +0 -0
  105. admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_maps.scssc +0 -0
  106. admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_settings.scssc +0 -0
  107. admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_validation.scssc +0 -0
  108. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_background.scssc +0 -0
  109. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_bleed.scssc +0 -0
  110. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_box-sizing.scssc +0 -0
  111. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_breakpoint-plugin.scssc +0 -0
  112. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_container.scssc +0 -0
  113. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_context.scssc +0 -0
  114. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_gallery.scssc +0 -0
  115. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_grids.scssc +0 -0
  116. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_gutters.scssc +0 -0
  117. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_isolate.scssc +0 -0
  118. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_margins.scssc +0 -0
  119. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_padding.scssc +0 -0
  120. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_rows.scssc +0 -0
  121. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_settings.scssc +0 -0
  122. admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_span.scssc +0 -0
  123. frontend/js/leadin.js +53 -11
  124. images/power-up-icon-constant-contact-list-sync.png +0 -0
  125. images/power-up-icon-constant-contact-list-sync@2x.png +0 -0
  126. images/power-up-icon-constant-contact-list-sync_small.png +0 -0
  127. images/power-up-icon-constant-contact-list-sync_small@2x.png +0 -0
  128. inc/class-emailer.php +10 -15
  129. inc/class-leadin-options.php +0 -149
  130. inc/leadin-ajax-functions.php +65 -45
  131. inc/leadin-functions.php +154 -2
  132. leadin.php +35 -13
  133. lib/mixpanel/Base/LI_MixpanelBase.php +68 -0
  134. lib/mixpanel/Base/MixpanelBase.php +5 -0
  135. lib/mixpanel/ConsumerStrategies/AbstractConsumer.php +2 -0
  136. lib/mixpanel/ConsumerStrategies/CurlConsumer.php +2 -0
  137. lib/mixpanel/ConsumerStrategies/FileConsumer.php +2 -0
  138. lib/mixpanel/ConsumerStrategies/LI_AbstractConsumer.php +58 -0
  139. lib/mixpanel/ConsumerStrategies/LI_CurlConsumer.php +222 -0
  140. lib/mixpanel/ConsumerStrategies/LI_FileConsumer.php +39 -0
  141. lib/mixpanel/ConsumerStrategies/LI_SocketConsumer.php +309 -0
  142. lib/mixpanel/ConsumerStrategies/SocketConsumer.php +2 -0
  143. lib/mixpanel/{Mixpanel.php → LI_Mixpanel.php} +17 -14
  144. lib/mixpanel/Producers/LI_MixpanelBaseProducer.php +230 -0
  145. lib/mixpanel/Producers/LI_MixpanelEvents.php +165 -0
  146. lib/mixpanel/Producers/LI_MixpanelPeople.php +139 -0
  147. lib/mixpanel/Producers/MixpanelBaseProducer.php +5 -3
  148. lib/mixpanel/Producers/MixpanelEvents.php +2 -0
  149. lib/mixpanel/Producers/MixpanelPeople.php +2 -0
  150. power-ups/constant-contact-list-sync.php +131 -0
  151. power-ups/constant-contact-list-sync/admin/constant-contact-list-sync-admin.php +205 -0
  152. power-ups/constant-contact-list-sync/inc/li_constant_contact.php +1114 -0
  153. power-ups/contacts/admin/contacts-admin.php +3 -3
  154. power-ups/mailchimp-list-sync.php +1 -1
  155. power-ups/mailchimp-list-sync/admin/mailchimp-list-sync-admin.php +8 -7
  156. power-ups/mailchimp-list-sync/inc/MailChimp-API.php +1 -1
  157. power-ups/subscribe-widget.php +8 -2
  158. power-ups/subscribe-widget/admin/subscribe-widget-admin.php +81 -8
  159. power-ups/subscribe-widget/frontend/css/leadin-subscribe.css +0 -47
  160. power-ups/subscribe-widget/frontend/js/leadin-subscribe.js +6 -5
  161. readme.txt +47 -5
  162. screenshot-4.png +0 -0
admin/inc/class-leadin-contact.php CHANGED
@@ -65,6 +65,7 @@ class LI_Contact {
65
  FROM
66
  li_pageviews
67
  WHERE
 
68
  lead_hashkey LIKE %s ORDER BY event_date DESC", '%b %D', '%b %D %l:%i%p', $this->hashkey);
69
 
70
  $pageviews = $wpdb->get_results($q, ARRAY_A);
@@ -81,6 +82,7 @@ class LI_Contact {
81
  FROM
82
  li_submissions
83
  WHERE
 
84
  lead_hashkey = '%s' ORDER BY event_date DESC", '%b %D %l:%i%p', $this->hashkey);
85
 
86
  $submissions = $wpdb->get_results($q, ARRAY_A);
@@ -118,7 +120,7 @@ class LI_Contact {
118
  $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_date'] = $event['event_date'];
119
 
120
  // Set the first submission if it's not set and then leave it alone
121
- if ( !$lead->first_visit )
122
  $lead->first_visit = $event['event_date'];
123
 
124
  // Always overwrite the last_submission date which will end as last submission date
@@ -147,7 +149,7 @@ class LI_Contact {
147
  $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['activities'][] = $event;
148
 
149
  // Set the first submission if it's not set and then leave it alone
150
- if ( !$lead->first_submission )
151
  $lead->first_submission = $event['event_date'];
152
 
153
  // Always overwrite the last_submission date which will end as last submission date
65
  FROM
66
  li_pageviews
67
  WHERE
68
+ pageview_deleted = 0 AND
69
  lead_hashkey LIKE %s ORDER BY event_date DESC", '%b %D', '%b %D %l:%i%p', $this->hashkey);
70
 
71
  $pageviews = $wpdb->get_results($q, ARRAY_A);
82
  FROM
83
  li_submissions
84
  WHERE
85
+ form_deleted = 0 AND
86
  lead_hashkey = '%s' ORDER BY event_date DESC", '%b %D %l:%i%p', $this->hashkey);
87
 
88
  $submissions = $wpdb->get_results($q, ARRAY_A);
120
  $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_date'] = $event['event_date'];
121
 
122
  // Set the first submission if it's not set and then leave it alone
123
+ if ( ! isset($lead->first_visit) )
124
  $lead->first_visit = $event['event_date'];
125
 
126
  // Always overwrite the last_submission date which will end as last submission date
149
  $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['activities'][] = $event;
150
 
151
  // Set the first submission if it's not set and then leave it alone
152
+ if ( ! isset($lead->first_submission) )
153
  $lead->first_submission = $event['event_date'];
154
 
155
  // Always overwrite the last_submission date which will end as last submission date
admin/inc/class-leadin-list-table.php CHANGED
@@ -187,32 +187,33 @@ class LI_List_Table extends WP_List_Table {
187
  $ids_to_delete .= ',';
188
  }
189
 
190
- $q = $wpdb->prepare("SELECT hashkey FROM li_leads WHERE lead_id IN ( " . $ids_to_delete . " )", $ids_to_delete);
191
- $hashes = $wpdb->get_results($q);
192
 
193
- for ( $i = 0; $i < count($hashes); $i++ )
194
  {
 
 
 
195
 
196
- $hashes_to_delete .= "'". $hashes[$i]->hashkey. "'";
197
-
198
- if ( $i != (count($hashes)-1) )
199
- $hashes_to_delete .= ",";
200
- }
201
 
202
- //Detect when a bulk action is being triggered...
203
- if( 'delete' === $this->current_action() )
204
- {
205
- $q = $wpdb->prepare("DELETE FROM li_pageviews WHERE lead_hashkey IN (" . $hashes_to_delete . ")", "");
206
- $delete_pageviews = $wpdb->query($q);
207
 
208
- $q = $wpdb->prepare("DELETE FROM li_submissions WHERE lead_hashkey IN (" . $hashes_to_delete . ")", "");
209
- $delete_submissions = $wpdb->query($q);
210
 
211
- $q = $wpdb->prepare("DELETE FROM li_leads WHERE lead_id IN (" . $ids_to_delete . ")", "");
212
- $delete_leads = $wpdb->query($q);
 
213
  }
214
  }
215
-
216
  }
217
 
218
  /**
@@ -252,7 +253,7 @@ class LI_List_Table extends WP_List_Table {
252
  li_leads l
253
  LEFT JOIN li_submissions s ON l.hashkey = s.lead_hashkey
254
  LEFT JOIN li_pageviews p ON l.hashkey = p.lead_hashkey
255
- WHERE l.lead_email != ''", '%Y/%m/%d %l:%i%p', '%Y/%m/%d %l:%i%p');
256
 
257
  $q .= $mysql_contact_type_filter;
258
  $q .= ( $mysql_search_filter ? $mysql_search_filter : "" );
@@ -264,7 +265,7 @@ class LI_List_Table extends WP_List_Table {
264
 
265
  foreach ( $leads as $lead )
266
  {
267
- $q = $wpdb->prepare("SELECT COUNT(DISTINCT pageview_id) FROM li_pageviews WHERE lead_hashkey = %s AND pageview_session_start = 1", $lead->hashkey);
268
  $pageviews = $wpdb->get_var($q);
269
  $lead->lead_visits = $pageviews;
270
 
@@ -275,6 +276,9 @@ class LI_List_Table extends WP_List_Table {
275
  else if ( $lead->lead_status == 'comment' )
276
  $lead_status = 'Commenter';
277
 
 
 
 
278
  $lead_array = array(
279
  'ID' => $lead->lead_id,
280
  'email' => sprintf('<a href="?page=%s&action=%s&lead=%s">' . "<img class='pull-left leadin-contact-avatar' src='https://app.getsignals.com/avatar/image/?emails=" . $lead->lead_email . "' width='35' height='35'/> " . '</a>', $_REQUEST['page'], 'view', $lead->lead_id) . sprintf('<a href="?page=%s&action=%s&lead=%s"><b>' . $lead->lead_email . '</b></a>', $_REQUEST['page'], 'view', $lead->lead_id),
@@ -284,7 +288,7 @@ class LI_List_Table extends WP_List_Table {
284
  'pageviews' => $lead->lead_pageviews,
285
  'date' => $lead->lead_date,
286
  'last_visit' => $lead->last_visit,
287
- 'source' => ( $lead->lead_source ? "<a title='Visit page' href='" . $lead->lead_source . "' target='_blank'>" . $lead->lead_source . "</a>" : 'Direct' )
288
  );
289
 
290
  array_push($all_leads, $lead_array);
@@ -303,13 +307,13 @@ class LI_List_Table extends WP_List_Table {
303
  $q = $wpdb->prepare("
304
  SELECT
305
  COUNT(DISTINCT lead_email) AS total_contacts,
306
- ( SELECT COUNT(DISTINCT lead_email) FROM li_leads WHERE lead_status = 'lead' AND lead_email != '' ) AS total_leads,
307
- ( SELECT COUNT(DISTINCT lead_email) FROM li_leads WHERE lead_status = 'comment' AND lead_email != '' ) AS total_comments,
308
- ( SELECT COUNT(DISTINCT lead_email) FROM li_leads WHERE lead_status = 'subscribe' AND lead_email != '' ) AS total_subscribes
309
  FROM
310
  li_leads
311
  WHERE
312
- lead_email != ''", "");
313
 
314
  $totals = $wpdb->get_row($q);
315
  return $totals;
@@ -420,12 +424,12 @@ class LI_List_Table extends WP_List_Table {
420
  $this->process_bulk_action();
421
  $this->data = $this->get_leads();;
422
 
423
- $orderby = ( !empty($_REQUEST['orderby']) ? $_REQUEST['orderby'] : 'date' );
424
  $order = ( !empty($_REQUEST['order']) ? $_REQUEST['order'] : 'desc' );
425
 
426
  function usort_reorder($a,$b)
427
  {
428
- $orderby = ( !empty($_REQUEST['orderby']) ? $_REQUEST['orderby'] : 'date' );
429
  $order = ( !empty($_REQUEST['order']) ? $_REQUEST['order'] : 'desc' );
430
 
431
  if ( $a[$orderby] == $b[$orderby] )
187
  $ids_to_delete .= ',';
188
  }
189
 
190
+ $q = $wpdb->prepare("SELECT hashkey FROM li_leads WHERE lead_id IN ( " . $ids_to_delete . " )", "");
191
+ $hashes = $wpdb->get_results($q);
192
 
193
+ if ( count($hashes) )
194
  {
195
+ for ( $i = 0; $i < count($hashes); $i++ )
196
+ {
197
+ $hashes_to_delete .= "'". $hashes[$i]->hashkey. "'";
198
 
199
+ if ( $i != (count($hashes)-1) )
200
+ $hashes_to_delete .= ",";
201
+ }
 
 
202
 
203
+ //Detect when a bulk action is being triggered...
204
+ if( 'delete' === $this->current_action() )
205
+ {
206
+ $q = $wpdb->prepare("UPDATE li_pageviews SET pageview_deleted = 1 WHERE lead_hashkey IN (" . $hashes_to_delete . ")", "");
207
+ $delete_pageviews = $wpdb->query($q);
208
 
209
+ $q = $wpdb->prepare("UPDATE li_submissions SET form_deleted = 1 WHERE lead_hashkey IN (" . $hashes_to_delete . ")", "");
210
+ $delete_submissions = $wpdb->query($q);
211
 
212
+ $q = $wpdb->prepare("UPDATE li_leads SET lead_deleted = 1 WHERE lead_id IN (" . $ids_to_delete . ")", "");
213
+ $delete_leads = $wpdb->query($q);
214
+ }
215
  }
216
  }
 
217
  }
218
 
219
  /**
253
  li_leads l
254
  LEFT JOIN li_submissions s ON l.hashkey = s.lead_hashkey
255
  LEFT JOIN li_pageviews p ON l.hashkey = p.lead_hashkey
256
+ WHERE l.lead_email != '' AND l.lead_deleted = 0 ", '%Y/%m/%d %l:%i%p', '%Y/%m/%d %l:%i%p');
257
 
258
  $q .= $mysql_contact_type_filter;
259
  $q .= ( $mysql_search_filter ? $mysql_search_filter : "" );
265
 
266
  foreach ( $leads as $lead )
267
  {
268
+ $q = $wpdb->prepare("SELECT COUNT(DISTINCT pageview_id) FROM li_pageviews WHERE lead_hashkey = %s AND pageview_session_start = 1 AND pageview_deleted = 0", $lead->hashkey);
269
  $pageviews = $wpdb->get_var($q);
270
  $lead->lead_visits = $pageviews;
271
 
276
  else if ( $lead->lead_status == 'comment' )
277
  $lead_status = 'Commenter';
278
 
279
+ $url_parts = parse_url($lead->lead_source);
280
+ $url = urldecode(rtrim($url_parts['host'] . '/' . $url_parts['path'], '/'));
281
+
282
  $lead_array = array(
283
  'ID' => $lead->lead_id,
284
  'email' => sprintf('<a href="?page=%s&action=%s&lead=%s">' . "<img class='pull-left leadin-contact-avatar' src='https://app.getsignals.com/avatar/image/?emails=" . $lead->lead_email . "' width='35' height='35'/> " . '</a>', $_REQUEST['page'], 'view', $lead->lead_id) . sprintf('<a href="?page=%s&action=%s&lead=%s"><b>' . $lead->lead_email . '</b></a>', $_REQUEST['page'], 'view', $lead->lead_id),
288
  'pageviews' => $lead->lead_pageviews,
289
  'date' => $lead->lead_date,
290
  'last_visit' => $lead->last_visit,
291
+ 'source' => ( $lead->lead_source ? "<a title='Visit page' href='" . $lead->lead_source . "' target='_blank'>" . $url . "</a>" : 'Direct' )
292
  );
293
 
294
  array_push($all_leads, $lead_array);
307
  $q = $wpdb->prepare("
308
  SELECT
309
  COUNT(DISTINCT lead_email) AS total_contacts,
310
+ ( SELECT COUNT(DISTINCT lead_email) FROM li_leads WHERE lead_status = 'lead' AND lead_email != '' AND lead_deleted = 0 ) AS total_leads,
311
+ ( SELECT COUNT(DISTINCT lead_email) FROM li_leads WHERE lead_status = 'comment' AND lead_email != '' AND lead_deleted = 0 ) AS total_comments,
312
+ ( SELECT COUNT(DISTINCT lead_email) FROM li_leads WHERE lead_status = 'subscribe' AND lead_email != '' AND lead_deleted = 0 ) AS total_subscribes
313
  FROM
314
  li_leads
315
  WHERE
316
+ lead_email != '' AND lead_deleted = 0", "");
317
 
318
  $totals = $wpdb->get_row($q);
319
  return $totals;
424
  $this->process_bulk_action();
425
  $this->data = $this->get_leads();;
426
 
427
+ $orderby = ( !empty($_REQUEST['orderby']) ? $_REQUEST['orderby'] : 'last_visit' );
428
  $order = ( !empty($_REQUEST['order']) ? $_REQUEST['order'] : 'desc' );
429
 
430
  function usort_reorder($a,$b)
431
  {
432
+ $orderby = ( !empty($_REQUEST['orderby']) ? $_REQUEST['orderby'] : 'last_visit' );
433
  $order = ( !empty($_REQUEST['order']) ? $_REQUEST['order'] : 'desc' );
434
 
435
  if ( $a[$orderby] == $b[$orderby] )
admin/js/leadin-admin.js CHANGED
@@ -1,3 +1,3 @@
1
  jQuery(document).ready( function ( $ ) {
2
-
3
  });
1
  jQuery(document).ready( function ( $ ) {
2
+
3
  });
admin/leadin-admin.php CHANGED
@@ -138,11 +138,8 @@ class WPLeadInAdmin {
138
  function leadin_options_section_heading ( )
139
  {
140
  ?>
141
- <div id="message" class="updated below-h2">
142
- <p>Visitor tracking is <span style='color: #090; font-weight: bold;'>installed and tracking visitors</span>.</p>
143
- <p>The next time a visitor fills out a form on your WordPress site with an email address, LeadIn will send you an email with the contact's referral source and page view history.</p>
144
- <p>All of your visitor's form submissions are stored in your <a href='<?php echo get_bloginfo('wpurl');?>/wp-admin/admin.php?page=leadin_contacts'>LeadIn Contacts</a>.</p>
145
- </div>
146
  <?php
147
 
148
  $this->print_hidden_settings_fields();
@@ -156,6 +153,10 @@ class WPLeadInAdmin {
156
  $li_db_version = ( $options['li_db_version'] ? $options['li_db_version'] : LEADIN_DB_VERSION );
157
  $ignore_settings_popup = ( $options['ignore_settings_popup'] ? $options['ignore_settings_popup'] : 0 );
158
  $onboarding_complete = ( $options['onboarding_complete'] ? $options['onboarding_complete'] : 0 );
 
 
 
 
159
 
160
  printf(
161
  '<input id="li_installed" type="hidden" name="leadin_options[li_installed]" value="%d"/>',
@@ -176,6 +177,16 @@ class WPLeadInAdmin {
176
  '<input id="onboarding_complete" type="hidden" name="leadin_options[onboarding_complete]" value="%d"/>',
177
  $onboarding_complete
178
  );
 
 
 
 
 
 
 
 
 
 
179
  }
180
 
181
  function update_option_leadin_options_callback ( $old_value, $new_value )
@@ -303,12 +314,12 @@ class WPLeadInAdmin {
303
  function li_email_callback ()
304
  {
305
  $options = get_option('leadin_options');
306
- $li_email = ( $options['li_email'] ? $options['li_email'] : get_bloginfo('admin_email') ); // Get email from plugin settings, if none set, use admin email
307
-
308
  printf(
309
- '<input id="li_email" type="text" id="title" name="leadin_options[li_email]" value="%s" size="50"/><br/><span class="description">Separate multiple emails with commas</span>',
310
  $li_email
311
- );
312
  }
313
 
314
  /**
@@ -475,8 +486,13 @@ class WPLeadInAdmin {
475
  {
476
  ?>
477
  <div id="leadin-footer">
478
- <p class="support"><a href="http://leadin.com">LeadIn</a> <?php echo LEADIN_PLUGIN_VERSION?> | Need help? <a href="#" onclick="return SnapEngage.startLink();">Contact us</a>.</p>
 
 
 
 
479
  <p class="sharing"><a href="https://twitter.com/leadinapp" class="twitter-follow-button" data-show-count="false">Follow @leadinapp</a>
 
480
  <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script></p>
481
  </div>
482
  <!-- begin SnapEngage code -->
138
  function leadin_options_section_heading ( )
139
  {
140
  ?>
141
+ <p style='color: #090; font-weight: bold;'>Visitor tracking is installed and tracking visitors.</p>
142
+ <p>The next time a visitor fills out a form on your WordPress site with an email address, LeadIn will send you an email with the contact's referral source and page view history.</p>
 
 
 
143
  <?php
144
 
145
  $this->print_hidden_settings_fields();
153
  $li_db_version = ( $options['li_db_version'] ? $options['li_db_version'] : LEADIN_DB_VERSION );
154
  $ignore_settings_popup = ( $options['ignore_settings_popup'] ? $options['ignore_settings_popup'] : 0 );
155
  $onboarding_complete = ( $options['onboarding_complete'] ? $options['onboarding_complete'] : 0 );
156
+ $data_recovered = ( $options['data_recovered'] ? $options['data_recovered'] : 0 );
157
+ $delete_flags_fixed = ( $options['delete_flags_fixed'] ? $options['delete_flags_fixed'] : 0 );
158
+
159
+
160
 
161
  printf(
162
  '<input id="li_installed" type="hidden" name="leadin_options[li_installed]" value="%d"/>',
177
  '<input id="onboarding_complete" type="hidden" name="leadin_options[onboarding_complete]" value="%d"/>',
178
  $onboarding_complete
179
  );
180
+
181
+ printf(
182
+ '<input id="data_recovered" type="hidden" name="leadin_options[data_recovered]" value="%d"/>',
183
+ $data_recovered
184
+ );
185
+
186
+ printf(
187
+ '<input id="delete_flags_fixed" type="hidden" name="leadin_options[delete_flags_fixed]" value="%d"/>',
188
+ $delete_flags_fixed
189
+ );
190
  }
191
 
192
  function update_option_leadin_options_callback ( $old_value, $new_value )
314
  function li_email_callback ()
315
  {
316
  $options = get_option('leadin_options');
317
+ $li_email = ( isset($options['li_email']) && $options['li_email'] ? $options['li_email'] : '' ); // Get email from plugin settings, if none set, use admin email
318
+
319
  printf(
320
+ '<input id="li_email" type="text" id="title" name="leadin_options[li_email]" value="%s" size="50"/><br/><span class="description">Separate multiple emails with commas. Leave blank to disable email notifications.</span>',
321
  $li_email
322
+ );
323
  }
324
 
325
  /**
486
  {
487
  ?>
488
  <div id="leadin-footer">
489
+ <p class="support">
490
+ <a href="http://leadin.com">LeadIn</a> <?php echo LEADIN_PLUGIN_VERSION?>
491
+ <span style="padding: 0px 5px;">|</span> Need help? <a href="#" onclick="return SnapEngage.startLink();">Contact us</a>
492
+ <span style="padding: 0px 5px;">|</span> Love Leadin? <a href="http://wordpress.org/support/view/plugin-reviews/leadin?rate=5#postform">Review us</a>
493
+ </p>
494
  <p class="sharing"><a href="https://twitter.com/leadinapp" class="twitter-follow-button" data-show-count="false">Follow @leadinapp</a>
495
+
496
  <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script></p>
497
  </div>
498
  <!-- begin SnapEngage code -->
admin/sass/.sass-cache/068a0951e0f44f5f7f1ab84833b671b75a0d1dd1/_clearfix.scssc DELETED
Binary file
admin/sass/.sass-cache/068a0951e0f44f5f7f1ab84833b671b75a0d1dd1/_hacks.scssc DELETED
Binary file
admin/sass/.sass-cache/0c3aa292b717031700a0344f604e0b99b479fc79/_susy.scssc DELETED
Binary file
admin/sass/.sass-cache/0c3aa292b717031700a0344f604e0b99b479fc79/_susyone.scssc DELETED
Binary file
admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_container.scssc DELETED
Binary file
admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_end.scssc DELETED
Binary file
admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_isolate.scssc DELETED
Binary file
admin/sass/.sass-cache/38f6ef42965a6f3d0db78c73e0225721577524c6/_span.scssc DELETED
Binary file
admin/sass/.sass-cache/52467c9ff883780d88c16e980bb8d43c6f3f113f/_susy.scssc DELETED
Binary file
admin/sass/.sass-cache/52467c9ff883780d88c16e980bb8d43c6f3f113f/_susyone.scssc DELETED
Binary file
admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_background.scssc DELETED
Binary file
admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_box-sizing.scssc DELETED
Binary file
admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_clearfix.scssc DELETED
Binary file
admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_prefix.scssc DELETED
Binary file
admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_rem.scssc DELETED
Binary file
admin/sass/.sass-cache/5b0ded2c21a8382c081077cb9f130cd662c5ce8e/_support.scssc DELETED
Binary file
admin/sass/.sass-cache/5cb96857c5f923ac31761ca34fd727781525b482/_math.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_animation.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_appearance.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_background-clip.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_background-origin.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_background-size.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_border-radius.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_box-shadow.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_box-sizing.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_columns.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_filter.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_flexbox.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_font-face.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_hyphenation.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_images.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_inline-block.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_opacity.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_regions.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_text-shadow.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_transform.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_transition.scssc DELETED
Binary file
admin/sass/.sass-cache/652c361299b1c66982df1f07df999b752c83fdf9/_user-interface.scssc DELETED
Binary file
admin/sass/.sass-cache/797a4968f0b8e5805efbffb5db3326f524464ef6/_float.scssc DELETED
Binary file
admin/sass/.sass-cache/797a4968f0b8e5805efbffb5db3326f524464ef6/_shared.scssc DELETED
Binary file
admin/sass/.sass-cache/797a4968f0b8e5805efbffb5db3326f524464ef6/_support.scssc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_contact_detail_page.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_contacts_list_page.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_grid.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_powerups_page.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_settings_page.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_tables.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/_variables.sassc DELETED
Binary file
admin/sass/.sass-cache/7feb88049015a4dff396a24786f85814ae06425c/leadin-admin.sassc DELETED
Binary file
admin/sass/.sass-cache/a1d71d7b609c5e9852c5bfc005fe6aac02f7590f/_css3.scssc DELETED
Binary file
admin/sass/.sass-cache/a1d71d7b609c5e9852c5bfc005fe6aac02f7590f/_support.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_appearance.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-clip.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-origin.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-size.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_border-radius.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-shadow.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-sizing.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_columns.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_filter.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_font-face.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_hyphenation.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_images.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_inline-block.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_opacity.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_regions.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_shared.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_text-shadow.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transform.scssc DELETED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transition.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_background.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_functions.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_grid.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_isolation.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_margin.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_media.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_padding.scssc DELETED
Binary file
admin/sass/.sass-cache/a3876a0695ee0091b635892c2b539b865026ee33/_settings.scssc DELETED
Binary file
admin/sass/.sass-cache/a9f64e5d5dd6c28c118d0281ab7b3986e2729ea7/_grid-background.scssc DELETED
Binary file
admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_clearfix.scssc DELETED
Binary file
admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_hacks.scssc DELETED
Binary file
admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_css3.scssc DELETED
Binary file
admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_support.scssc DELETED
Binary file
admin/sass/.sass-cache/e4b1f93193ae29c5c808f60975dad0a21092da77/_units.scssc DELETED
Binary file
admin/sass/.sass-cache/e4b1f93193ae29c5c808f60975dad0a21092da77/_vertical_rhythm.scssc DELETED
Binary file
admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_background.scssc DELETED
Binary file
admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_container.scssc DELETED
Binary file
admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_direction.scssc DELETED
Binary file
admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_margins.scssc DELETED
Binary file
admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_output.scssc DELETED
Binary file
admin/sass/.sass-cache/e84f61336d179205ae6530d80482c1682976ab4e/_padding.scssc DELETED
Binary file
admin/sass/.sass-cache/eba4af504693c8b2c03a74241844b1c5ab414495/_su.scssc DELETED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contact_detail_page.sassc DELETED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contacts_list_page.sassc DELETED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_settings_page.sassc DELETED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_tables.sassc DELETED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_variables.sassc DELETED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/leadin-admin.sassc DELETED
Binary file
admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_grid.scssc DELETED
Binary file
admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_maps.scssc DELETED
Binary file
admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_settings.scssc DELETED
Binary file
admin/sass/.sass-cache/fc4f4268225416ca556db305ea34784cf502478b/_validation.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_background.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_bleed.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_box-sizing.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_breakpoint-plugin.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_container.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_context.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_gallery.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_grids.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_gutters.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_isolate.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_margins.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_padding.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_rows.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_settings.scssc DELETED
Binary file
admin/sass/.sass-cache/fd9de46f5118e2ba495163d7bc711998018af17d/_span.scssc DELETED
Binary file
frontend/js/leadin.js CHANGED
@@ -12,10 +12,22 @@ jQuery(document).ready( function ( $ ) {
12
  if ( li_submission_cookie )
13
  {
14
  var submission_data = JSON.parse(li_submission_cookie);
15
- leadin_insert_form_submission(submission_data.submission_hash, submission_data.hashkey, submission_data.page_title, submission_data.page_url, submission_data.json_form_fields, submission_data.lead_email, submission_data.form_submission_type, function ( data ) {
16
- // Form was submitted successfully before page reload. Delete cookie for this submission
17
- $.removeCookie('li_submission', {path: "/", domain: ""});
18
- });
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
 
21
  if ( !hashkey )
@@ -63,8 +75,11 @@ function leadin_submit_form ( $form, $, form_type )
63
  {
64
  var $this = $form;
65
 
66
- var form_fields = [];
67
- var lead_email = '';
 
 
 
68
  var form_submission_type = ( form_type ? form_type : 'lead' );
69
 
70
  // Excludes hidden input fields + submit inputs
@@ -163,6 +178,15 @@ function leadin_submit_form ( $form, $, form_type )
163
 
164
  if ( $value.indexOf('@') != -1 && $value.indexOf('.') != -1 && !lead_email )
165
  lead_email = $value;
 
 
 
 
 
 
 
 
 
166
  });
167
 
168
  var radio_groups = [];
@@ -249,6 +273,9 @@ function leadin_submit_form ( $form, $, form_type )
249
  "submission_hash": submission_hash,
250
  "hashkey": hashkey,
251
  "lead_email": lead_email,
 
 
 
252
  "page_title": page_title,
253
  "page_url": page_url,
254
  "json_form_fields": json_form_fields,
@@ -257,10 +284,22 @@ function leadin_submit_form ( $form, $, form_type )
257
 
258
  $.cookie("li_submission", JSON.stringify(form_submission), {path: "/", domain: ""});
259
 
260
- leadin_insert_form_submission(submission_hash, hashkey, page_title, page_url, json_form_fields, lead_email, form_submission_type, function ( data ) {
261
- // Form was executed 100% successfully before page reload. Delete cookie for this submission
262
- $.removeCookie('li_submission', {path: "/", domain: ""});
263
- });
 
 
 
 
 
 
 
 
 
 
 
 
264
  }
265
  else // No lead - submit form as usual
266
  {
@@ -349,7 +388,7 @@ function leadin_insert_lead ( hashkey, page_referrer ) {
349
  });
350
  }
351
 
352
- function leadin_insert_form_submission ( submission_haskey, hashkey, page_title, page_url, json_fields, lead_email, form_submission_type, Callback )
353
  {
354
  jQuery.ajax({
355
  type: 'POST',
@@ -362,6 +401,9 @@ function leadin_insert_form_submission ( submission_haskey, hashkey, page_title,
362
  "li_url": page_url,
363
  "li_fields": json_fields,
364
  "li_email": lead_email,
 
 
 
365
  "li_submission_type": form_submission_type
366
  },
367
  success: function(data){
12
  if ( li_submission_cookie )
13
  {
14
  var submission_data = JSON.parse(li_submission_cookie);
15
+ leadin_insert_form_submission(
16
+ submission_data.submission_hash,
17
+ submission_data.hashkey,
18
+ submission_data.page_title,
19
+ submission_data.page_url,
20
+ submission_data.json_form_fields,
21
+ submission_data.lead_email,
22
+ submission_data.lead_first_name,
23
+ submission_data.lead_last_name,
24
+ submission_data.lead_phone,
25
+ submission_data.form_submission_type,
26
+ function ( data ) {
27
+ // Form was submitted successfully before page reload. Delete cookie for this submission
28
+ $.removeCookie('li_submission', {path: "/", domain: ""});
29
+ }
30
+ );
31
  }
32
 
33
  if ( !hashkey )
75
  {
76
  var $this = $form;
77
 
78
+ var form_fields = [];
79
+ var lead_email = '';
80
+ var lead_first_name = '';
81
+ var lead_last_name = '';
82
+ var lead_phone = '';
83
  var form_submission_type = ( form_type ? form_type : 'lead' );
84
 
85
  // Excludes hidden input fields + submit inputs
178
 
179
  if ( $value.indexOf('@') != -1 && $value.indexOf('.') != -1 && !lead_email )
180
  lead_email = $value;
181
+
182
+ if ( $element.attr('id') == 'leadin-subscribe-fname')
183
+ lead_first_name = $value;
184
+
185
+ if ( $element.attr('id') == 'leadin-subscribe-lname')
186
+ lead_last_name = $value;
187
+
188
+ if ( $element.attr('id') == 'leadin-subscribe-phone')
189
+ lead_phone = $value;
190
  });
191
 
192
  var radio_groups = [];
273
  "submission_hash": submission_hash,
274
  "hashkey": hashkey,
275
  "lead_email": lead_email,
276
+ "lead_first_name": lead_first_name,
277
+ "lead_last_name": lead_last_name,
278
+ "lead_phone": lead_phone,
279
  "page_title": page_title,
280
  "page_url": page_url,
281
  "json_form_fields": json_form_fields,
284
 
285
  $.cookie("li_submission", JSON.stringify(form_submission), {path: "/", domain: ""});
286
 
287
+ leadin_insert_form_submission(
288
+ submission_hash,
289
+ hashkey,
290
+ page_title,
291
+ page_url,
292
+ json_form_fields,
293
+ lead_email,
294
+ lead_first_name,
295
+ lead_last_name,
296
+ lead_phone,
297
+ form_submission_type,
298
+ function ( data ) {
299
+ // Form was executed 100% successfully before page reload. Delete cookie for this submission
300
+ $.removeCookie('li_submission', {path: "/", domain: ""});
301
+ }
302
+ );
303
  }
304
  else // No lead - submit form as usual
305
  {
388
  });
389
  }
390
 
391
+ function leadin_insert_form_submission ( submission_haskey, hashkey, page_title, page_url, json_fields, lead_email, lead_first_name, lead_last_name, lead_phone, form_submission_type, Callback )
392
  {
393
  jQuery.ajax({
394
  type: 'POST',
401
  "li_url": page_url,
402
  "li_fields": json_fields,
403
  "li_email": lead_email,
404
+ "li_first_name": lead_first_name,
405
+ "li_last_name": lead_last_name,
406
+ "li_phone": lead_phone,
407
  "li_submission_type": form_submission_type
408
  },
409
  success: function(data){
images/power-up-icon-constant-contact-list-sync.png ADDED
Binary file
images/power-up-icon-constant-contact-list-sync@2x.png ADDED
Binary file
images/power-up-icon-constant-contact-list-sync_small.png ADDED
Binary file
images/power-up-icon-constant-contact-list-sync_small@2x.png ADDED
Binary file
inc/class-emailer.php CHANGED
@@ -95,9 +95,10 @@ class LI_Emailer {
95
  // Each line in an email can only be 998 characters long, so lines need to be broken with a wordwrap
96
  $body = wordwrap($body, 900, "\r\n");
97
 
 
98
  $headers = "From: LeadIn <team@leadin.com>\r\n";
99
- $headers.= "Reply-To: LeadIn <team@leadin.com>\r\n";
100
- $headers.= "X-Mailer: PHP/" . phpversion()."\r\n";
101
  $headers.= "MIME-Version: 1.0\r\n";
102
  $headers.= "Content-type: text/html; charset=utf-8\r\n";
103
 
@@ -108,27 +109,19 @@ class LI_Emailer {
108
  if ( $history->submission->form_type == "comment" )
109
  {
110
  $subject = "New comment posted on " . $history->submission->form_page_title;
111
- leadin_track_plugin_activity("New comment");
112
  $subject .= " by " . $history->lead->lead_email;
113
  }
114
  else if ( $history->submission->form_type == "subscribe" )
115
  {
116
  $subject = "New subscriber from " . $history->submission->form_page_title;
117
- leadin_track_plugin_activity("New subscriber");
118
  $this->send_subscriber_confirmation_email($history);
119
  }
120
  else
121
  {
122
  if ( $history->new_contact )
123
- {
124
  $subject = "New lead from " . get_bloginfo('name') . " - Say hello to " . $history->lead->lead_email;
125
- leadin_track_plugin_activity("New lead");
126
- }
127
  else
128
- {
129
  $subject = "Lead from " . get_bloginfo('name') . " - Say hello again to " . $history->lead->lead_email;
130
- leadin_track_plugin_activity("Returning lead");
131
- }
132
  }
133
 
134
  $email_sent = wp_mail($to, $subject, $body, $headers);
@@ -154,6 +147,7 @@ class LI_Emailer {
154
  FROM
155
  li_pageviews
156
  WHERE
 
157
  lead_hashkey LIKE %s ORDER BY pageview_date ASC", '%b %e', '%b %e %l:%i%p', $hashkey);
158
 
159
  $pageviews = $wpdb->get_results($q);
@@ -173,10 +167,10 @@ class LI_Emailer {
173
  $count++;
174
  }
175
 
176
- $q = $wpdb->prepare("SELECT form_date AS form_datetime, DATE_FORMAT(form_date, %s) AS form_date, form_page_title, form_page_url, form_fields, form_type FROM li_submissions WHERE lead_hashkey = '%s' ORDER BY form_datetime DESC", '%b %e %l:%i%p', $hashkey);
177
  $submissions = $wpdb->get_results($q);
178
 
179
- $q = $wpdb->prepare("SELECT lead_id, lead_date, lead_ip, lead_source, lead_email, lead_status FROM li_leads WHERE hashkey LIKE %s AND lead_email != ''", $hashkey);
180
  $lead = $wpdb->get_row($q);
181
 
182
  $history = (object)NULL;
@@ -416,13 +410,14 @@ class LI_Emailer {
416
 
417
  // Email Base close
418
  $body .= '</center></td></tr></table></body></html>';
 
419
 
420
  // Each line in an email can only be 998 characters long, so lines need to be broken with a wordwrap
421
  $body = wordwrap($body, 900, "\r\n");
422
 
423
- $headers = "From: LeadIn <team@leadin.com>\r\n";
424
- $headers.= "Reply-To: LeadIn <team@leadin.com>\r\n";
425
- $headers.= "X-Mailer: PHP/" . phpversion()."\r\n";
426
  $headers.= "MIME-Version: 1.0\r\n";
427
  $headers.= "Content-type: text/html; charset=utf-8\r\n";
428
 
95
  // Each line in an email can only be 998 characters long, so lines need to be broken with a wordwrap
96
  $body = wordwrap($body, 900, "\r\n");
97
 
98
+ $from = $history->lead->lead_email;
99
  $headers = "From: LeadIn <team@leadin.com>\r\n";
100
+ $headers.= "Reply-To: LeadIn <" . $from . ">\r\n";
101
+ $headers.= "X-Mailer: PHP/" . phpversion() . "\r\n";
102
  $headers.= "MIME-Version: 1.0\r\n";
103
  $headers.= "Content-type: text/html; charset=utf-8\r\n";
104
 
109
  if ( $history->submission->form_type == "comment" )
110
  {
111
  $subject = "New comment posted on " . $history->submission->form_page_title;
 
112
  $subject .= " by " . $history->lead->lead_email;
113
  }
114
  else if ( $history->submission->form_type == "subscribe" )
115
  {
116
  $subject = "New subscriber from " . $history->submission->form_page_title;
 
117
  $this->send_subscriber_confirmation_email($history);
118
  }
119
  else
120
  {
121
  if ( $history->new_contact )
 
122
  $subject = "New lead from " . get_bloginfo('name') . " - Say hello to " . $history->lead->lead_email;
 
 
123
  else
 
124
  $subject = "Lead from " . get_bloginfo('name') . " - Say hello again to " . $history->lead->lead_email;
 
 
125
  }
126
 
127
  $email_sent = wp_mail($to, $subject, $body, $headers);
147
  FROM
148
  li_pageviews
149
  WHERE
150
+ pageview_deleted = 0 AND
151
  lead_hashkey LIKE %s ORDER BY pageview_date ASC", '%b %e', '%b %e %l:%i%p', $hashkey);
152
 
153
  $pageviews = $wpdb->get_results($q);
167
  $count++;
168
  }
169
 
170
+ $q = $wpdb->prepare("SELECT form_date AS form_datetime, DATE_FORMAT(form_date, %s) AS form_date, form_page_title, form_page_url, form_fields, form_type FROM li_submissions WHERE lead_hashkey = '%s' AND form_deleted = 0 ORDER BY form_datetime DESC", '%b %e %l:%i%p', $hashkey);
171
  $submissions = $wpdb->get_results($q);
172
 
173
+ $q = $wpdb->prepare("SELECT lead_id, lead_date, lead_ip, lead_source, lead_email, lead_status FROM li_leads WHERE hashkey LIKE %s AND lead_email != '' AND lead_deleted = 0", $hashkey);
174
  $lead = $wpdb->get_row($q);
175
 
176
  $history = (object)NULL;
410
 
411
  // Email Base close
412
  $body .= '</center></td></tr></table></body></html>';
413
+ $from = apply_filters( 'li_subscribe_from', $leadin_email );
414
 
415
  // Each line in an email can only be 998 characters long, so lines need to be broken with a wordwrap
416
  $body = wordwrap($body, 900, "\r\n");
417
 
418
+ $headers = "From: LeadIn <" . $from . ">\r\n";
419
+ $headers.= "Reply-To: LeadIn <" . $from . ">\r\n";
420
+ $headers.= "X-Mailer: PHP/" . phpversion() . "\r\n";
421
  $headers.= "MIME-Version: 1.0\r\n";
422
  $headers.= "Content-type: text/html; charset=utf-8\r\n";
423
 
inc/class-leadin-options.php DELETED
@@ -1,149 +0,0 @@
1
- <?php
2
-
3
- class LeadInOptions {
4
-
5
- /*public static function get_option_names( $type = 'compact' ) {
6
- switch ( $type ) {
7
- case 'non-compact' :
8
- case 'non_compact' :
9
- return array(
10
- 'register',
11
- 'activated',
12
- 'active_modules',
13
- 'do_activate',
14
- 'log',
15
- 'publicize',
16
- 'widget_twitter',
17
- 'wpcc_options',
18
- );
19
- }
20
-
21
- return array(
22
- 'id', // (int) The Client ID/WP.com Blog ID of this site.
23
- 'blog_token', // (string) The Client Secret/Blog Token of this site.
24
- 'user_token', // (string) The User Token of this site. (deprecated)
25
- 'publicize_connections', // (array) An array of Publicize connections from WordPress.com
26
- 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
27
- 'user_tokens', // (array) User Tokens for each user of this site who has connected to jetpack.wordpress.com.
28
- 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time
29
- 'old_version', // (string) Used to determine which modules are the most recently added. previous_version:time
30
- 'fallback_no_verify_ssl_certs', // (int) Flag for determining if this host must skip SSL Certificate verification due to misconfigured SSL.
31
- 'time_diff', // (int) Offset between Jetpack server's clocks and this server's clocks. Jetpack Server Time = time() + (int) Jetpack_Options::get_option( 'time_diff' )
32
- 'public', // (int|bool) If we think this site is public or not (1, 0), false if we haven't yet tried to figure it out.
33
- 'videopress', // (array) VideoPress options array.
34
- 'is_network_site', // (int|bool) If we think this site is a network or a single blog (1, 0), false if we haven't yet tried to figue it out.
35
- 'social_links', // (array) The specified links for each social networking site.
36
- 'identity_crisis_whitelist', // (array) An array of options, each having an array of the values whitelisted for it.
37
- 'gplus_authors', // (array) The Google+ authorship information for connected users.
38
- 'last_heartbeat', // (int) The timestamp of the last heartbeat that fired.
39
- );
40
- }*/
41
-
42
- /**
43
- * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate.
44
- *
45
- * @param string $name Option name
46
- * @param mixed $default (optional)
47
- */
48
- public static function get_option( $name, $default = false ) {
49
- if ( in_array( $name, self::get_option_names( 'non_compact' ) ) ) {
50
- return get_option( "jetpack_$name" );
51
- } else if ( !in_array( $name, self::get_option_names() ) ) {
52
- trigger_error( sprintf( 'Invalid Jetpack option name: %s', $name ), E_USER_WARNING );
53
- return false;
54
- }
55
-
56
- $options = get_option( 'jetpack_options' );
57
- if ( is_array( $options ) && isset( $options[$name] ) ) {
58
- return $options[$name];
59
- }
60
-
61
- return $default;
62
- }
63
-
64
- /**
65
- * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate.
66
- *
67
- * @param string $name Option name
68
- * @param mixed $value Option value
69
- */
70
- public static function update_option( $name, $value ) {
71
- if ( in_array( $name, self::get_option_names( 'non_compact' ) ) ) {
72
- return update_option( "jetpack_$name", $value );
73
- } else if ( !in_array( $name, self::get_option_names() ) ) {
74
- trigger_error( sprintf( 'Invalid Jetpack option name: %s', $name ), E_USER_WARNING );
75
- return false;
76
- }
77
-
78
- $options = get_option( 'jetpack_options' );
79
- if ( !is_array( $options ) ) {
80
- $options = array();
81
- }
82
-
83
- $options[$name] = $value;
84
-
85
- return update_option( 'jetpack_options', $options );
86
- }
87
-
88
- /**
89
- * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate.
90
- *
91
- * @param array $array array( option name => option value, ... )
92
- */
93
- public static function update_options( $array ) {
94
- $names = array_keys( $array );
95
-
96
- foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ) ) as $unknown_name ) {
97
- trigger_error( sprintf( 'Invalid Jetpack option name: %s', $unknown_name ), E_USER_WARNING );
98
- unset( $array[$unknown_name] );
99
- }
100
-
101
- foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
102
- update_option( "jetpack_$name", $array[$name] );
103
- unset( $array[$name] );
104
- }
105
-
106
- $options = get_option( 'jetpack_options' );
107
- if ( !is_array( $options ) ) {
108
- $options = array();
109
- }
110
-
111
- return update_option( 'jetpack_options', array_merge( $options, $array ) );
112
- }
113
-
114
- /**
115
- * Deletes the given option. May be passed multiple option names as an array.
116
- * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
117
- *
118
- * @param string|array $names
119
- */
120
- public static function delete_option( $names ) {
121
- $names = (array) $names;
122
-
123
- foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ) ) as $unknown_name ) {
124
- trigger_error( sprintf( 'Invalid Jetpack option name: %s', $unknown_name ), E_USER_WARNING );
125
- }
126
-
127
- foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
128
- delete_option( "jetpack_$name" );
129
- }
130
-
131
- $options = get_option( 'jetpack_options' );
132
- if ( !is_array( $options ) ) {
133
- $options = array();
134
- }
135
-
136
- $to_delete = array_intersect( $names, self::get_option_names(), array_keys( $options ) );
137
- if ( $to_delete ) {
138
- foreach ( $to_delete as $name ) {
139
- unset( $options[$name] );
140
- }
141
-
142
- return update_option( 'jetpack_options', $options );
143
- }
144
-
145
- return true;
146
- }
147
-
148
- }
149
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/leadin-ajax-functions.php CHANGED
@@ -7,7 +7,7 @@ if ( !defined('LEADIN_PLUGIN_VERSION') )
7
  }
8
 
9
  /**
10
- * Check if the cookied hashkey has been merged with another contact
11
  *
12
  * @echo Hashkey from a merged_hashkeys row, FALSE if hashkey does not exist in a merged_hashkeys row
13
  */
@@ -17,10 +17,11 @@ function leadin_check_merged_contact ()
17
 
18
  $stale_hash = $_POST['li_id'];
19
 
 
20
  $q = $wpdb->prepare("SELECT hashkey, merged_hashkeys FROM li_leads WHERE merged_hashkeys LIKE '%%%s%%'", like_escape($stale_hash));
21
  $row = $wpdb->get_row($q);
22
 
23
- if ( isset($row->hashkey) )
24
  {
25
  // One final update to set all the previous pageviews to the new hashkey
26
  $q = $wpdb->prepare("UPDATE li_pageviews SET lead_hashkey = %s WHERE lead_hashkey = %s", $row->hashkey, $stale_hash);
@@ -31,13 +32,12 @@ function leadin_check_merged_contact ()
31
  $wpdb->query($q);
32
 
33
  // Remove the passed hash from the merged hashkeys for the row
34
- $merged_hashkeys = explode(',', $row->merged_hashkeys);
35
- $merged_hashkeys = leadin_array_delete($merged_hashkeys, "'" . $stale_hash . "'");
36
 
37
- $q = $wpdb->prepare("UPDATE li_leads SET merged_hashkeys = %s WHERE hashkey = %s", implode(',', $merged_hashkeys), $row->hashkey);
38
- $wpdb->query($q);
39
 
40
- $q = $wpdb->prepare("DELETE FROM li_leads WHERE hashkey LIKE '%%%s%%'", like_escape($stale_hash));
41
  $wpdb->query($q);
42
 
43
  echo json_encode($row->hashkey);
@@ -134,16 +134,20 @@ function leadin_insert_form_submission ()
134
  $page_url = $_POST['li_url'];
135
  $form_json = $_POST['li_fields'];
136
  $email = $_POST['li_email'];
 
 
 
137
  $submission_type = $_POST['li_submission_type'];
138
  $options = get_option('leadin_options');
139
- $li_admin_email = ( $options['li_email'] ) ? $options['li_email'] : get_bloginfo('admin_email');
140
 
141
  // Check to see if the form_hashkey exists, and if it does, don't run the insert or send the email
142
- $q = $wpdb->prepare("SELECT form_hashkey FROM li_submissions WHERE form_hashkey = %s", $submission_hash);
143
  $submission_hash_exists = $wpdb->get_var($q);
144
 
145
  if ( $submission_hash_exists )
146
  {
 
147
  return 1;
148
  exit;
149
  }
@@ -151,18 +155,20 @@ function leadin_insert_form_submission ()
151
  // Don't send the lead email when an administrator is leaving a comment or when the commenter's email is the same as the leadin email
152
  if ( !(current_user_can('administrator') && $submission_type == 'comment') && !(strstr($li_admin_email, $email) && $submission_type == 'comment') )
153
  {
154
- $q = $wpdb->prepare("SELECT * FROM li_leads WHERE hashkey = %s", $hashkey);
 
155
  $contact = $wpdb->get_row($q);
156
 
157
  // Check for existing contacts based on whether the email is present in the contacts table
158
- $q = $wpdb->prepare("SELECT lead_email, hashkey, merged_hashkeys, lead_status FROM li_leads WHERE lead_email = %s AND hashkey != %s", $email, $hashkey);
159
  $existing_contacts = $wpdb->get_results($q);
160
 
 
161
  $existing_contact_status = 'lead';
162
 
163
  // Setup the string for the existing hashkeys
164
  $existing_contact_hashkeys = $contact->merged_hashkeys;
165
- if ( $contact->merged_hashkeys )
166
  $existing_contact_hashkeys .= ',';
167
 
168
  // Do some merging if the email exists already in the contact table
@@ -173,9 +179,11 @@ function leadin_insert_form_submission ()
173
  // Start with the existing contact's hashkeys and create a string containg comma-deliminated hashes
174
  $existing_contact_hashkeys .= "'" . $existing_contacts[$i]->hashkey . "'";
175
 
 
176
  if ( $existing_contacts[$i]->merged_hashkeys )
177
  $existing_contact_hashkeys .= "," . $existing_contacts[$i]->merged_hashkeys;
178
 
 
179
  if ( $i != count($existing_contacts)-1 )
180
  $existing_contact_hashkeys .= ",";
181
 
@@ -188,6 +196,12 @@ function leadin_insert_form_submission ()
188
  $existing_contact_status = 'subscribe';
189
  }
190
 
 
 
 
 
 
 
191
  // Update all the previous pageviews to the new hashkey
192
  $q = $wpdb->prepare("UPDATE li_pageviews SET lead_hashkey = %s WHERE lead_hashkey IN ( $existing_contact_hashkeys )", $hashkey);
193
  $wpdb->query($q);
@@ -195,22 +209,26 @@ function leadin_insert_form_submission ()
195
  // Update all the previous submissions to the new hashkey
196
  $q = $wpdb->prepare("UPDATE li_submissions SET lead_hashkey = %s WHERE lead_hashkey IN ( $existing_contact_hashkeys )", $hashkey);
197
  $wpdb->query($q);
 
 
 
 
198
  }
199
 
200
- // Prevent duplicates by deleting existing submission if it didn't finish the process before the web page refreshed
201
- $q = $wpdb->prepare("DELETE FROM li_submissions WHERE form_hashkey = %s", $submission_hash);
202
  $wpdb->query($q);
203
 
204
  // Insert the form fields and hash into the submissions table
205
  $result = $wpdb->insert(
206
  'li_submissions',
207
  array(
208
- 'form_hashkey' => $submission_hash,
209
- 'lead_hashkey' => $hashkey,
210
- 'form_page_title' => $page_title,
211
- 'form_page_url' => $page_url,
212
- 'form_fields' => $form_json,
213
- 'form_type' => $submission_type
214
  ),
215
  array(
216
  '%s', '%s', '%s', '%s', '%s', '%s'
@@ -244,13 +262,35 @@ function leadin_insert_form_submission ()
244
  if ( in_array('mailchimp_list_sync', $active_power_ups) )
245
  {
246
  global $leadin_mailchimp_list_sync_wp;
247
- $leadin_mailchimp_list_sync_wp->push_mailchimp_subscriber_to_list($email);
 
 
 
 
 
 
248
  }
249
  }
250
 
251
- // Send the contact email
252
- $li_emailer = new LI_Emailer();
253
- $li_emailer->send_new_lead_email($hashkey);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
  return $rows_updated;
256
  }
@@ -270,7 +310,7 @@ function leadin_check_visitor_status ()
270
 
271
  $hash = $_POST['li_id'];
272
 
273
- $q = $wpdb->prepare("SELECT lead_status FROM li_leads WHERE hashkey = %s", $hash);
274
  $lead_status = $wpdb->get_var($q);
275
 
276
  if ( isset($lead_status) )
@@ -297,26 +337,6 @@ function leadin_subscribe_show ()
297
  {
298
  leadin_track_plugin_activity('widget shown');
299
  die();
300
-
301
- /*global $wpdb;
302
-
303
- $hash = $_POST['li_id'];
304
-
305
- $q = $wpdb->prepare("SELECT lead_status FROM li_leads WHERE hashkey = %s", $hash);
306
- $lead_status = $wpdb->get_var($q);
307
-
308
- if ( isset($lead_status) )
309
- {
310
- echo json_encode($lead_status);
311
- die();
312
- }
313
- else
314
- {
315
- echo json_encode(FALSE);
316
- die();
317
- }*/
318
-
319
-
320
  }
321
 
322
  add_action('wp_ajax_leadin_subscribe_show', 'leadin_subscribe_show'); // Call when user logged in
7
  }
8
 
9
  /**
10
+ * Check if the cookied hashkey has been merged with another contact. If it is, set visitor's cookie to new hashkey
11
  *
12
  * @echo Hashkey from a merged_hashkeys row, FALSE if hashkey does not exist in a merged_hashkeys row
13
  */
17
 
18
  $stale_hash = $_POST['li_id'];
19
 
20
+ // Check if hashkey is in a merged contact
21
  $q = $wpdb->prepare("SELECT hashkey, merged_hashkeys FROM li_leads WHERE merged_hashkeys LIKE '%%%s%%'", like_escape($stale_hash));
22
  $row = $wpdb->get_row($q);
23
 
24
+ if ( isset($row->hashkey) && $stale_hash )
25
  {
26
  // One final update to set all the previous pageviews to the new hashkey
27
  $q = $wpdb->prepare("UPDATE li_pageviews SET lead_hashkey = %s WHERE lead_hashkey = %s", $row->hashkey, $stale_hash);
32
  $wpdb->query($q);
33
 
34
  // Remove the passed hash from the merged hashkeys for the row
35
+ $merged_hashkeys = array_unique(array_filter(explode(',', $row->merged_hashkeys)));
 
36
 
37
+ // Delete the stale hash from the merged hashkeys array
38
+ $merged_hashkeys = leadin_array_delete($merged_hashkeys, "'" . $stale_hash . "'");
39
 
40
+ $q = $wpdb->prepare("UPDATE li_leads SET merged_hashkeys = %s WHERE hashkey = %s", rtrim(implode(',', $merged_hashkeys), ','), $row->hashkey);
41
  $wpdb->query($q);
42
 
43
  echo json_encode($row->hashkey);
134
  $page_url = $_POST['li_url'];
135
  $form_json = $_POST['li_fields'];
136
  $email = $_POST['li_email'];
137
+ $first_name = $_POST['li_first_name'];
138
+ $last_name = $_POST['li_last_name'];
139
+ $phone = $_POST['li_phone'];
140
  $submission_type = $_POST['li_submission_type'];
141
  $options = get_option('leadin_options');
142
+ $li_admin_email = ( isset($options['li_email']) ) ? $options['li_email'] : '';
143
 
144
  // Check to see if the form_hashkey exists, and if it does, don't run the insert or send the email
145
+ $q = $wpdb->prepare("SELECT form_hashkey FROM li_submissions WHERE form_hashkey = %s AND form_deleted = 0", $submission_hash);
146
  $submission_hash_exists = $wpdb->get_var($q);
147
 
148
  if ( $submission_hash_exists )
149
  {
150
+ // The form has been inserted successful so send back a trigger to clear the cached submission cookie on the front end
151
  return 1;
152
  exit;
153
  }
155
  // Don't send the lead email when an administrator is leaving a comment or when the commenter's email is the same as the leadin email
156
  if ( !(current_user_can('administrator') && $submission_type == 'comment') && !(strstr($li_admin_email, $email) && $submission_type == 'comment') )
157
  {
158
+ // Get the contact row tied to hashkey
159
+ $q = $wpdb->prepare("SELECT * FROM li_leads WHERE hashkey = %s AND lead_deleted = 0", $hashkey);
160
  $contact = $wpdb->get_row($q);
161
 
162
  // Check for existing contacts based on whether the email is present in the contacts table
163
+ $q = $wpdb->prepare("SELECT lead_email, hashkey, merged_hashkeys, lead_status FROM li_leads WHERE lead_email = %s AND hashkey != %s AND lead_deleted = 0", $email, $hashkey);
164
  $existing_contacts = $wpdb->get_results($q);
165
 
166
+ // Set the default contact life cycle status to lead
167
  $existing_contact_status = 'lead';
168
 
169
  // Setup the string for the existing hashkeys
170
  $existing_contact_hashkeys = $contact->merged_hashkeys;
171
+ if ( $contact->merged_hashkeys && count($existing_contacts) )
172
  $existing_contact_hashkeys .= ',';
173
 
174
  // Do some merging if the email exists already in the contact table
179
  // Start with the existing contact's hashkeys and create a string containg comma-deliminated hashes
180
  $existing_contact_hashkeys .= "'" . $existing_contacts[$i]->hashkey . "'";
181
 
182
+ // Add any of those existing contact row's merged hashkeys
183
  if ( $existing_contacts[$i]->merged_hashkeys )
184
  $existing_contact_hashkeys .= "," . $existing_contacts[$i]->merged_hashkeys;
185
 
186
+ // Add a comma delimiter
187
  if ( $i != count($existing_contacts)-1 )
188
  $existing_contact_hashkeys .= ",";
189
 
196
  $existing_contact_status = 'subscribe';
197
  }
198
 
199
+ // Remove duplicates from the array
200
+ $existing_contact_hashkeys = implode(',', array_unique(explode(',', $existing_contact_hashkeys)));
201
+
202
+ // Safety precaution - trim any trailing commas
203
+ $existing_contact_hashkeys = rtrim($existing_contact_hashkeys, ',');
204
+
205
  // Update all the previous pageviews to the new hashkey
206
  $q = $wpdb->prepare("UPDATE li_pageviews SET lead_hashkey = %s WHERE lead_hashkey IN ( $existing_contact_hashkeys )", $hashkey);
207
  $wpdb->query($q);
209
  // Update all the previous submissions to the new hashkey
210
  $q = $wpdb->prepare("UPDATE li_submissions SET lead_hashkey = %s WHERE lead_hashkey IN ( $existing_contact_hashkeys )", $hashkey);
211
  $wpdb->query($q);
212
+
213
+ // "Delete" all the old leads from the leads table
214
+ $q = $wpdb->prepare("UPDATE li_leads SET lead_deleted = 1 WHERE hashkey IN ( $existing_contact_hashkeys )", "");
215
+ $wpdb->query($q);
216
  }
217
 
218
+ // Prevent duplicate form submission entries by deleting existing submissions if it didn't finish the process before the web page refreshed
219
+ $q = $wpdb->prepare("UPDATE li_submissions SET form_deleted = 1 WHERE form_hashkey = %s", $submission_hash);
220
  $wpdb->query($q);
221
 
222
  // Insert the form fields and hash into the submissions table
223
  $result = $wpdb->insert(
224
  'li_submissions',
225
  array(
226
+ 'form_hashkey' => $submission_hash,
227
+ 'lead_hashkey' => $hashkey,
228
+ 'form_page_title' => $page_title,
229
+ 'form_page_url' => $page_url,
230
+ 'form_fields' => $form_json,
231
+ 'form_type' => $submission_type
232
  ),
233
  array(
234
  '%s', '%s', '%s', '%s', '%s', '%s'
262
  if ( in_array('mailchimp_list_sync', $active_power_ups) )
263
  {
264
  global $leadin_mailchimp_list_sync_wp;
265
+ $leadin_mailchimp_list_sync_wp->push_mailchimp_subscriber_to_list($email, $first_name, $last_name, $phone);
266
+ }
267
+
268
+ if ( in_array('constant_contact_list_sync', $active_power_ups) )
269
+ {
270
+ global $leadin_constant_contact_list_sync_wp;
271
+ $leadin_constant_contact_list_sync_wp->push_constant_contact_subscriber_to_list($email, $first_name, $last_name, $phone);
272
  }
273
  }
274
 
275
+ if ( $contact_status == "comment" )
276
+ leadin_track_plugin_activity("New comment");
277
+ else if ( $contact_status == "subscribe" )
278
+ leadin_track_plugin_activity("New subscriber");
279
+ else
280
+ {
281
+ $history = $this->get_lead_history($hashkey);
282
+ if ( $history->new_contact )
283
+ leadin_track_plugin_activity("New lead");
284
+ else
285
+ leadin_track_plugin_activity("Returning lead");
286
+ }
287
+
288
+ if ( $li_admin_email )
289
+ {
290
+ // Send the contact email
291
+ $li_emailer = new LI_Emailer();
292
+ $li_emailer->send_new_lead_email($hashkey);
293
+ }
294
 
295
  return $rows_updated;
296
  }
310
 
311
  $hash = $_POST['li_id'];
312
 
313
+ $q = $wpdb->prepare("SELECT lead_status FROM li_leads WHERE hashkey = %s AND lead_deleted = 0", $hash);
314
  $lead_status = $wpdb->get_var($q);
315
 
316
  if ( isset($lead_status) )
337
  {
338
  leadin_track_plugin_activity('widget shown');
339
  die();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  }
341
 
342
  add_action('wp_ajax_leadin_subscribe_show', 'leadin_subscribe_show'); // Call when user logged in
inc/leadin-functions.php CHANGED
@@ -113,8 +113,11 @@ function leadin_get_current_user ()
113
  function leadin_register_user ()
114
  {
115
  $leadin_user = leadin_get_current_user();
116
- $mp = Mixpanel::getInstance(MIXPANEL_PROJECT_TOKEN);
 
 
117
 
 
118
  // @push mixpanel event for updated email
119
  $mp->identify($leadin_user['user_id']);
120
  $mp->createAlias( $leadin_user['user_id'], $leadin_user['alias']);
@@ -193,7 +196,7 @@ function leadin_track_plugin_activity ( $activity_desc )
193
  get_currentuserinfo();
194
  $user_id = md5(get_bloginfo('wpurl'));
195
 
196
- $mp = Mixpanel::getInstance(MIXPANEL_PROJECT_TOKEN);
197
  $mp->track($activity_desc, array("distinct_id" => $user_id, '$wp-url' => get_bloginfo('wpurl'), '$wp-version' => $wp_version, '$li-version' => LEADIN_PLUGIN_VERSION));
198
 
199
  return true;
@@ -247,4 +250,153 @@ function leadin_get_value_by_key ( $key_value, $array )
247
 
248
  return null;
249
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  ?>
113
  function leadin_register_user ()
114
  {
115
  $leadin_user = leadin_get_current_user();
116
+ return false;
117
+
118
+ $mp = new LI_Mixpanel(MIXPANEL_PROJECT_TOKEN);
119
 
120
+
121
  // @push mixpanel event for updated email
122
  $mp->identify($leadin_user['user_id']);
123
  $mp->createAlias( $leadin_user['user_id'], $leadin_user['alias']);
196
  get_currentuserinfo();
197
  $user_id = md5(get_bloginfo('wpurl'));
198
 
199
+ $mp = new LI_Mixpanel(MIXPANEL_PROJECT_TOKEN);
200
  $mp->track($activity_desc, array("distinct_id" => $user_id, '$wp-url' => get_bloginfo('wpurl'), '$wp-version' => $wp_version, '$li-version' => LEADIN_PLUGIN_VERSION));
201
 
202
  return true;
250
 
251
  return null;
252
  }
253
+
254
+ /**
255
+ * Data recovery algorithm for 0.7.2 upgrade
256
+ *
257
+ */
258
+ function leadin_recover_contact_data ()
259
+ {
260
+ global $wpdb;
261
+
262
+ $q = $wpdb->prepare("SELECT * FROM li_submissions WHERE form_fields LIKE '%%%s%%' AND form_fields LIKE '%%%s%%' AND form_deleted = 0", '@', '.');
263
+ $submissions = $wpdb->get_results($q);
264
+
265
+ if ( count($submissions) )
266
+ {
267
+ foreach ( $submissions as $submission )
268
+ {
269
+ $json = json_decode(stripslashes($submission->form_fields), TRUE);
270
+
271
+ foreach ( $json as $object )
272
+ {
273
+ if ( strstr($object['value'], '@') && strstr($object['value'], '@') && strlen($object['value']) <= 254 )
274
+ {
275
+ // check to see if the contact exists and if it does, skip the data recovery
276
+ $q = $wpdb->prepare("SELECT lead_email FROM li_leads WHERE lead_email = %s AND lead_deleted = 0", $object['value']);
277
+ $exists = $wpdb->get_var($q);
278
+
279
+ if ( $exists )
280
+ continue;
281
+
282
+ // get the original data
283
+ $q = $wpdb->prepare("SELECT pageview_date, pageview_source FROM li_pageviews WHERE lead_hashkey = %s AND pageview_deleted = 0 ORDER BY pageview_date ASC LIMIT 1", $submission->lead_hashkey);
284
+ $first_pageview = $wpdb->get_row($q);
285
+
286
+ // recreate the contact
287
+ $q = $wpdb->prepare("INSERT INTO li_leads ( lead_date, hashkey, lead_source, lead_email, lead_status ) VALUES ( %s, %s, %s, %s, %s )",
288
+ ( $first_pageview->pageview_date ? $first_pageview->pageview_date : $submission->form_date),
289
+ $submission->lead_hashkey,
290
+ ( $first_pageview->pageview_source ? $first_pageview->pageview_source : ''),
291
+ $object['value'],
292
+ $submission->form_type
293
+ );
294
+
295
+ $wpdb->query($q);
296
+ }
297
+ }
298
+ }
299
+ }
300
+
301
+ leadin_update_option('leadin_options', 'data_recovered', 1);
302
+ }
303
+
304
+ /**
305
+ * Algorithm to set deleted contacts flag for 0.8.3 upgrade
306
+ *
307
+ */
308
+ function leadin_delete_flag_fix ()
309
+ {
310
+ global $wpdb;
311
+
312
+ $q = $wpdb->prepare("SELECT lead_email, COUNT(hashkey) c FROM li_leads WHERE lead_email != '' GROUP BY lead_email HAVING c > 1", '');
313
+ $duplicates = $wpdb->get_results($q);
314
+
315
+ if ( count($duplicates) )
316
+ {
317
+ foreach ( $duplicates as $duplicate )
318
+ {
319
+ $existing_contact_status = 'lead';
320
+
321
+ $q = $wpdb->prepare("SELECT lead_email, hashkey, merged_hashkeys, lead_status FROM li_leads WHERE lead_email = %s AND lead_deleted = 0 ORDER BY lead_date DESC", $duplicate->lead_email);
322
+ $existing_contacts = $wpdb->get_results($q);
323
+
324
+ $newest = $existing_contacts[0];
325
+
326
+ // Setup the string for the existing hashkeys
327
+ $existing_contact_hashkeys = $newest->merged_hashkeys;
328
+ if ( $newest->merged_hashkeys && count($existing_contacts) )
329
+ $existing_contact_hashkeys .= ',';
330
+
331
+ // Do some merging if the email exists already in the contact table
332
+ if ( count($existing_contacts) )
333
+ {
334
+ for ( $i = 0; $i < count($existing_contacts); $i++ )
335
+ {
336
+ // Start with the existing contact's hashkeys and create a string containg comma-deliminated hashes
337
+ $existing_contact_hashkeys .= "'" . $existing_contacts[$i]->hashkey . "'";
338
+
339
+ // Add any of those existing contact row's merged hashkeys
340
+ if ( $existing_contacts[$i]->merged_hashkeys )
341
+ $existing_contact_hashkeys .= "," . $existing_contacts[$i]->merged_hashkeys;
342
+
343
+ // Add a comma delimiter
344
+ if ( $i != count($existing_contacts)-1 )
345
+ $existing_contact_hashkeys .= ",";
346
+
347
+ // Check on each existing lead if the lead_status is comment. If it is, save the status to override the new lead's status
348
+ if ( $existing_contacts[$i]->lead_status == 'comment' && $existing_contact_status == 'lead' )
349
+ $existing_contact_status = 'comment';
350
+
351
+ // Check on each existing lead if the lead_status is subscribe. If it is, save the status to override the new lead's status
352
+ if ( $existing_contacts[$i]->lead_status == 'subscribe' && ($existing_contact_status == 'lead' || $existing_contact_status == 'comment') )
353
+ $existing_contact_status = 'subscribe';
354
+ }
355
+ }
356
+
357
+ // Remove duplicates from the array and original hashkey just in case
358
+ $existing_contact_hashkeys = leadin_array_delete(array_unique(explode(',', $existing_contact_hashkeys)), "'" . $newest->hashkey . "'");
359
+
360
+ // Safety precaution - trim any trailing commas
361
+ $existing_contact_hashkey_string = rtrim(implode(',', $existing_contact_hashkeys), ',');
362
+
363
+ if ( $existing_contact_hashkey_string )
364
+ {
365
+ // Set the merged hashkeys with the fixed merged hashkey values
366
+ $q = $wpdb->prepare("UPDATE li_leads SET merged_hashkeys = %s, lead_status = %s WHERE hashkey = %s", $existing_contact_hashkey_string, $existing_contact_status, $newest->hashkey);
367
+ $wpdb->query($q);
368
+
369
+ // "Delete" all the old contacts
370
+ $q = $wpdb->prepare("UPDATE li_leads SET merged_hashkeys = '', lead_deleted = 1 WHERE hashkey IN ( $existing_contact_hashkey_string )", '');
371
+ $wpdb->query($q);
372
+
373
+ // Set all the pageviews and submissions to the new hashkey just in case
374
+ $q = $wpdb->prepare("UPDATE li_pageviews SET lead_hashkey = %s WHERE lead_hashkey IN ( $existing_contact_hashkey_string )", $newest->hashkey);
375
+ $wpdb->query($q);
376
+
377
+ // Update all the previous submissions to the new hashkey just in case
378
+ $q = $wpdb->prepare("UPDATE li_submissions SET lead_hashkey = %s WHERE lead_hashkey IN ( $existing_contact_hashkey_string )", $newest->hashkey);
379
+ $wpdb->query($q);
380
+ }
381
+ }
382
+ }
383
+
384
+ leadin_update_option('leadin_options', 'delete_flags_fixed', 1);
385
+ }
386
+
387
+ function sort_power_ups ( $power_ups, $ordered_power_ups )
388
+ {
389
+ $ordered = array();
390
+ $i = 0;
391
+ foreach ( $ordered_power_ups as $key )
392
+ {
393
+ if ( in_array($key, $power_ups) )
394
+ {
395
+ array_push($ordered, $key);
396
+ $i++;
397
+ }
398
+ }
399
+
400
+ return $ordered;
401
+ }
402
  ?>
leadin.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: LeadIn
4
  Plugin URI: http://leadin.com
5
  Description: LeadIn is an easy-to-use marketing automation and lead tracking plugin for WordPress that helps you better understand your web site visitors.
6
- Version: 0.7.0
7
  Author: Andy Cook, Nelson Joyce
8
  Author URI: http://leadin.com
9
  License: GPL2
@@ -23,10 +23,10 @@ if ( !defined('LEADIN_PLUGIN_SLUG') )
23
  define('LEADIN_PLUGIN_SLUG', basename(dirname(__FILE__)));
24
 
25
  if ( !defined('LEADIN_DB_VERSION') )
26
- define('LEADIN_DB_VERSION', '0.6.2');
27
 
28
  if ( !defined('LEADIN_PLUGIN_VERSION') )
29
- define('LEADIN_PLUGIN_VERSION', '0.7.0');
30
 
31
  if ( !defined('MIXPANEL_PROJECT_TOKEN') )
32
  define('MIXPANEL_PROJECT_TOKEN', 'a9615503ec58a6bce2c646a58390eac1');
@@ -43,7 +43,8 @@ require_once(LEADIN_PLUGIN_DIR . '/inc/leadin-functions.php');
43
  require_once(LEADIN_PLUGIN_DIR . '/power-ups/subscribe-widget.php');
44
  require_once(LEADIN_PLUGIN_DIR . '/power-ups/contacts.php');
45
  require_once(LEADIN_PLUGIN_DIR . '/power-ups/mailchimp-list-sync.php');
46
- require_once(LEADIN_PLUGIN_DIR . '/lib/mixpanel/Mixpanel.php');
 
47
 
48
  //=============================================
49
  // WPLeadIn Class
@@ -88,11 +89,13 @@ class WPLeadIn {
88
  if ( ($li_options['li_installed'] != 1) || (!is_array($li_options)) )
89
  {
90
  $opt = array(
91
- 'li_installed' => 1,
92
- 'li_db_version' => LEADIN_DB_VERSION,
93
- 'li_email' => get_bloginfo('admin_email'),
94
- 'onboarding_complete' => 0,
95
- 'ignore_settings_popup' => 0
 
 
96
  );
97
 
98
  update_option('leadin_options', $opt);
@@ -153,9 +156,10 @@ class WPLeadIn {
153
  `lead_email` varchar(255) DEFAULT NULL,
154
  `lead_status` set('lead','comment','subscribe') NOT NULL DEFAULT 'lead',
155
  `merged_hashkeys` text,
 
156
  PRIMARY KEY (`lead_id`),
157
  KEY `hashkey` (`hashkey`)
158
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=21 ;
159
 
160
  CREATE TABLE `li_pageviews` (
161
  `pageview_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
@@ -165,9 +169,10 @@ class WPLeadIn {
165
  `pageview_url` text NOT NULL,
166
  `pageview_source` text NOT NULL,
167
  `pageview_session_start` int(1) NOT NULL,
 
168
  PRIMARY KEY (`pageview_id`),
169
  KEY `lead_hashkey` (`lead_hashkey`)
170
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=692 ;
171
 
172
  CREATE TABLE `li_submissions` (
173
  `form_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
@@ -178,9 +183,10 @@ class WPLeadIn {
178
  `form_fields` text NOT NULL,
179
  `form_type` set('lead','comment','subscribe') NOT NULL DEFAULT 'lead',
180
  `form_hashkey` varchar(16) NOT NULL,
 
181
  PRIMARY KEY (`form_id`),
182
  KEY `lead_hashkey` (`lead_hashkey`)
183
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=85 ;";
184
 
185
  dbDelta($sql);
186
 
@@ -218,6 +224,12 @@ class WPLeadIn {
218
  update_option('leadin_active_power_ups', serialize($auto_activate));
219
  }
220
 
 
 
 
 
 
 
221
  // Set the database version if it doesn't exist
222
  if ( isset($li_options['li_db_version']) )
223
  {
@@ -237,6 +249,12 @@ class WPLeadIn {
237
  {
238
  $this->leadin_db_install();
239
  }
 
 
 
 
 
 
240
  }
241
 
242
  //=============================================
@@ -391,6 +409,10 @@ class WPLeadIn {
391
  $files[] = $file;
392
  }
393
 
 
 
 
 
394
  closedir( $dir );
395
 
396
  return $files;
@@ -399,7 +421,7 @@ class WPLeadIn {
399
  /**
400
  * Check whether or not a LeadIn power-up is active.
401
  *
402
- * @param string $power_up The slug of a Jetpack module.
403
  * @return bool
404
  *
405
  * @static
3
  Plugin Name: LeadIn
4
  Plugin URI: http://leadin.com
5
  Description: LeadIn is an easy-to-use marketing automation and lead tracking plugin for WordPress that helps you better understand your web site visitors.
6
+ Version: 0.8.3
7
  Author: Andy Cook, Nelson Joyce
8
  Author URI: http://leadin.com
9
  License: GPL2
23
  define('LEADIN_PLUGIN_SLUG', basename(dirname(__FILE__)));
24
 
25
  if ( !defined('LEADIN_DB_VERSION') )
26
+ define('LEADIN_DB_VERSION', '0.8.3');
27
 
28
  if ( !defined('LEADIN_PLUGIN_VERSION') )
29
+ define('LEADIN_PLUGIN_VERSION', '0.8.3');
30
 
31
  if ( !defined('MIXPANEL_PROJECT_TOKEN') )
32
  define('MIXPANEL_PROJECT_TOKEN', 'a9615503ec58a6bce2c646a58390eac1');
43
  require_once(LEADIN_PLUGIN_DIR . '/power-ups/subscribe-widget.php');
44
  require_once(LEADIN_PLUGIN_DIR . '/power-ups/contacts.php');
45
  require_once(LEADIN_PLUGIN_DIR . '/power-ups/mailchimp-list-sync.php');
46
+ require_once(LEADIN_PLUGIN_DIR . '/power-ups/constant-contact-list-sync.php');
47
+ require_once(LEADIN_PLUGIN_DIR . '/lib/mixpanel/LI_Mixpanel.php');
48
 
49
  //=============================================
50
  // WPLeadIn Class
89
  if ( ($li_options['li_installed'] != 1) || (!is_array($li_options)) )
90
  {
91
  $opt = array(
92
+ 'li_installed' => 1,
93
+ 'li_db_version' => LEADIN_DB_VERSION,
94
+ 'li_email' => get_bloginfo('admin_email'),
95
+ 'onboarding_complete' => 0,
96
+ 'ignore_settings_popup' => 0,
97
+ 'data_recovered' => 1,
98
+ 'delete_flags_fixed' => 1
99
  );
100
 
101
  update_option('leadin_options', $opt);
156
  `lead_email` varchar(255) DEFAULT NULL,
157
  `lead_status` set('lead','comment','subscribe') NOT NULL DEFAULT 'lead',
158
  `merged_hashkeys` text,
159
+ `lead_deleted` int(1) NOT NULL DEFAULT '0',
160
  PRIMARY KEY (`lead_id`),
161
  KEY `hashkey` (`hashkey`)
162
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
163
 
164
  CREATE TABLE `li_pageviews` (
165
  `pageview_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
169
  `pageview_url` text NOT NULL,
170
  `pageview_source` text NOT NULL,
171
  `pageview_session_start` int(1) NOT NULL,
172
+ `pageview_deleted` int(1) NOT NULL DEFAULT '0',
173
  PRIMARY KEY (`pageview_id`),
174
  KEY `lead_hashkey` (`lead_hashkey`)
175
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
176
 
177
  CREATE TABLE `li_submissions` (
178
  `form_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
183
  `form_fields` text NOT NULL,
184
  `form_type` set('lead','comment','subscribe') NOT NULL DEFAULT 'lead',
185
  `form_hashkey` varchar(16) NOT NULL,
186
+ `form_deleted` int(1) NOT NULL DEFAULT '0',
187
  PRIMARY KEY (`form_id`),
188
  KEY `lead_hashkey` (`lead_hashkey`)
189
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;";
190
 
191
  dbDelta($sql);
192
 
224
  update_option('leadin_active_power_ups', serialize($auto_activate));
225
  }
226
 
227
+ // 0.7.2 bug fix - data recovery algorithm for deleted contacts
228
+ if ( ! isset($li_options['data_recovered']) )
229
+ {
230
+ leadin_recover_contact_data();
231
+ }
232
+
233
  // Set the database version if it doesn't exist
234
  if ( isset($li_options['li_db_version']) )
235
  {
249
  {
250
  $this->leadin_db_install();
251
  }
252
+
253
+ // 0.8.3 bug fix - bug fix for duplicated contacts that should be merged
254
+ if ( ! isset($li_options['delete_flags_fixed']) )
255
+ {
256
+ leadin_delete_flag_fix();
257
+ }
258
  }
259
 
260
  //=============================================
409
  $files[] = $file;
410
  }
411
 
412
+ $files = sort_power_ups($files, array(
413
+ LEADIN_PLUGIN_DIR . '/power-ups/contacts' . '.php', LEADIN_PLUGIN_DIR . '/power-ups/subscribe-widget' . '.php', LEADIN_PLUGIN_DIR . '/power-ups/mailchimp-list-sync' . '.php', LEADIN_PLUGIN_DIR . '/power-ups/constant-contact-list-sync' . '.php'
414
+ ));
415
+
416
  closedir( $dir );
417
 
418
  return $files;
421
  /**
422
  * Check whether or not a LeadIn power-up is active.
423
  *
424
+ * @param string $power_up The slug of a power-up
425
  * @return bool
426
  *
427
  * @static
lib/mixpanel/Base/LI_MixpanelBase.php ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This a Base class which all Mixpanel classes extend from to provide some very basic
5
+ * debugging and logging functionality. It also serves to persist $_options across the library.
6
+ *
7
+ */
8
+
9
+
10
+
11
+ class LI_Base_MixpanelBase {
12
+
13
+
14
+ /**
15
+ * Default options that can be overridden via the $options constructor arg
16
+ * @var array
17
+ */
18
+ private $_defaults = array(
19
+ "max_batch_size" => 50, // the max batch size Mixpanel will accept is 50,
20
+ "max_queue_size" => 1000, // the max num of items to hold in memory before flushing
21
+ "debug" => false, // enable/disable debug mode
22
+ "consumer" => "curl", // which consumer to use
23
+ "host" => "api.mixpanel.com", // the host name for api calls
24
+ "events_endpoint" => "/track", // host relative endpoint for events
25
+ "people_endpoint" => "/engage", // host relative endpoint for people updates
26
+ "use_ssl" => true, // use ssl when available
27
+ "error_callback" => null // callback to use on consumption failures
28
+ );
29
+
30
+
31
+ /**
32
+ * An array of options to be used by the Mixpanel library.
33
+ * @var array
34
+ */
35
+ protected $_options = array();
36
+
37
+
38
+ /**
39
+ * Construct a new MixpanelBase object and merge custom options with defaults
40
+ * @param array $options
41
+ */
42
+ public function __construct($options = array()) {
43
+ $options = array_merge($this->_defaults, $options);
44
+ $this->_options = $options;
45
+ }
46
+
47
+
48
+ /**
49
+ * Log a message to PHP's error log
50
+ * @param $msg
51
+ */
52
+ protected function _log($msg) {
53
+ $arr = debug_backtrace();
54
+ $class = $arr[0]['class'];
55
+ $line = $arr[0]['line'];
56
+ error_log ( "[ $class - line $line ] : " . $msg );
57
+ }
58
+
59
+
60
+ /**
61
+ * Returns true if in debug mode, false if in production mode
62
+ * @return bool
63
+ */
64
+ protected function _debug() {
65
+ return array_key_exists("debug", $this->_options) && $this->_options["debug"] == true;
66
+ }
67
+
68
+ }
lib/mixpanel/Base/MixpanelBase.php CHANGED
@@ -1,10 +1,15 @@
1
  <?php
2
 
 
 
3
  /**
4
  * This a Base class which all Mixpanel classes extend from to provide some very basic
5
  * debugging and logging functionality. It also serves to persist $_options across the library.
6
  *
7
  */
 
 
 
8
  class Base_MixpanelBase {
9
 
10
 
1
  <?php
2
 
3
+ namespace LeadIn;
4
+
5
  /**
6
  * This a Base class which all Mixpanel classes extend from to provide some very basic
7
  * debugging and logging functionality. It also serves to persist $_options across the library.
8
  *
9
  */
10
+
11
+
12
+
13
  class Base_MixpanelBase {
14
 
15
 
lib/mixpanel/ConsumerStrategies/AbstractConsumer.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
  require_once(dirname(__FILE__) . "/../Base/MixpanelBase.php");
3
 
4
  /**
1
  <?php
2
+ namespace LeadIn;
3
+
4
  require_once(dirname(__FILE__) . "/../Base/MixpanelBase.php");
5
 
6
  /**
lib/mixpanel/ConsumerStrategies/CurlConsumer.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
  require_once(dirname(__FILE__) . "/AbstractConsumer.php");
3
 
4
  /**
1
  <?php
2
+ namespace LeadIn;
3
+
4
  require_once(dirname(__FILE__) . "/AbstractConsumer.php");
5
 
6
  /**
lib/mixpanel/ConsumerStrategies/FileConsumer.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
  require_once(dirname(__FILE__) . "/AbstractConsumer.php");
3
  /**
4
  * Consumes messages and writes them to a file
1
  <?php
2
+ namespace LeadIn;
3
+
4
  require_once(dirname(__FILE__) . "/AbstractConsumer.php");
5
  /**
6
  * Consumes messages and writes them to a file
lib/mixpanel/ConsumerStrategies/LI_AbstractConsumer.php ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(dirname(__FILE__) . "/../Base/LI_MixpanelBase.php");
4
+
5
+ /**
6
+ * Provides some base methods for use by a Consumer implementation
7
+ */
8
+ abstract class LI_ConsumerStrategies_AbstractConsumer extends LI_Base_MixpanelBase {
9
+
10
+ /**
11
+ * Creates a new AbstractConsumer
12
+ * @param array $options
13
+ */
14
+ function __construct($options = array()) {
15
+
16
+ parent::__construct($options);
17
+
18
+ if ($this->_debug()) {
19
+ $this->_log("Instantiated new Consumer");
20
+ }
21
+
22
+ }
23
+
24
+ /**
25
+ * Encode an array to be persisted
26
+ * @param array $params
27
+ * @return string
28
+ */
29
+ protected function _encode($params) {
30
+ return base64_encode(json_encode($params));
31
+ }
32
+
33
+ /**
34
+ * Handles errors that occur in a consumer
35
+ * @param $code
36
+ * @param $msg
37
+ */
38
+ protected function _handleError($code, $msg) {
39
+ if (isset($this->_options['error_callback'])) {
40
+ $handler = $this->_options['error_callback'];
41
+ call_user_func($handler, $code, $msg);
42
+ }
43
+
44
+ if ($this->_debug()) {
45
+ $arr = debug_backtrace();
46
+ $class = get_class($arr[0]['object']);
47
+ $line = $arr[0]['line'];
48
+ error_log ( "[ $class - line $line ] : " . print_r($msg, true) );
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Persist a batch of messages in whatever way the implementer sees fit
54
+ * @param array $batch an array of messages to consume
55
+ * @return boolean success or fail
56
+ */
57
+ abstract function persist($batch);
58
+ }
lib/mixpanel/ConsumerStrategies/LI_CurlConsumer.php ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(dirname(__FILE__) . "/LI_AbstractConsumer.php");
4
+
5
+ /**
6
+ * Consumes messages and sends them to a host/endpoint using cURL
7
+ */
8
+ class LI_ConsumerStrategies_CurlConsumer extends LI_ConsumerStrategies_AbstractConsumer {
9
+
10
+ /**
11
+ * @var string the host to connect to (e.g. api.mixpanel.com)
12
+ */
13
+ protected $_host;
14
+
15
+
16
+ /**
17
+ * @var string the host-relative endpoint to write to (e.g. /engage)
18
+ */
19
+ protected $_endpoint;
20
+
21
+
22
+ /**
23
+ * @var int connect_timeout The number of seconds to wait while trying to connect. Default is 5 seconds.
24
+ */
25
+ protected $_connect_timeout;
26
+
27
+
28
+ /**
29
+ * @var int timeout The maximum number of seconds to allow cURL call to execute. Default is 30 seconds.
30
+ */
31
+ protected $_timeout;
32
+
33
+
34
+ /**
35
+ * @var string the protocol to use for the cURL connection
36
+ */
37
+ protected $_protocol;
38
+
39
+
40
+ /**
41
+ * @var bool|null true to fork the cURL process (using exec) or false to use PHP's cURL extension. false by default
42
+ */
43
+ protected $_fork = null;
44
+
45
+
46
+ /**
47
+ * Creates a new LI_CurlConsumer and assigns properties from the $options array
48
+ * @param array $options
49
+ * @throws Exception
50
+ */
51
+ function __construct($options) {
52
+ parent::__construct($options);
53
+
54
+ $this->_host = $options['host'];
55
+ $this->_endpoint = $options['endpoint'];
56
+ $this->_connect_timeout = array_key_exists('connect_timeout', $options) ? $options['connect_timeout'] : 5;
57
+ $this->_timeout = array_key_exists('timeout', $options) ? $options['timeout'] : 30;
58
+ $this->_protocol = array_key_exists('use_ssl', $options) && $options['use_ssl'] == true ? "https" : "http";
59
+ $this->_fork = array_key_exists('fork', $options) ? ($options['fork'] == true) : false;
60
+
61
+ // ensure the environment is workable for the given settings
62
+ if ($this->_fork == true) {
63
+ $exists = function_exists('exec');
64
+ if (!$exists) {
65
+ throw new Exception('The "exec" function must exist to use the cURL consumer in "fork" mode. Try setting fork = false or use another consumer.');
66
+ }
67
+ $disabled = explode(', ', ini_get('disable_functions'));
68
+ $enabled = !in_array('exec', $disabled);
69
+ if (!$enabled) {
70
+ throw new Exception('The "exec" function must be enabled to use the cURL consumer in "fork" mode. Try setting fork = false or use another consumer.');
71
+ }
72
+ } else {
73
+ if (!function_exists('curl_init')) {
74
+ throw new Exception('The cURL PHP extension is required to use the cURL consumer with fork = false. Try setting fork = true or use another consumer.');
75
+ }
76
+ }
77
+ }
78
+
79
+
80
+ /**
81
+ * Write to the given host/endpoint using either a forked cURL process or using PHP's cURL extension
82
+ * @param array $batch
83
+ * @return bool
84
+ */
85
+ public function persist($batch) {
86
+ if (count($batch) > 0) {
87
+ $data = "data=" . $this->_encode($batch);
88
+ $url = $this->_protocol . "://" . $this->_host . $this->_endpoint;
89
+ if ($this->_fork) {
90
+ return $this->_execute_forked($url, $data);
91
+ } else {
92
+ return $this->_execute($url, $data);
93
+ }
94
+ } else {
95
+ return true;
96
+ }
97
+ }
98
+
99
+
100
+ /**
101
+ * Write using the cURL php extension
102
+ * @param $url
103
+ * @param $data
104
+ * @return bool
105
+ */
106
+ protected function _execute($url, $data) {
107
+ if ($this->_debug()) {
108
+ $this->_log("Making blocking cURL call to $url");
109
+ }
110
+
111
+ $ch = curl_init();
112
+ curl_setopt($ch, CURLOPT_URL, $url);
113
+ curl_setopt($ch, CURLOPT_HEADER, 0);
114
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_connect_timeout);
115
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout);
116
+ curl_setopt($ch, CURLOPT_POST, 1);
117
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
118
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
119
+ $response = curl_exec($ch);
120
+ if (false === $response) {
121
+ $curl_error = curl_error($ch);
122
+ $curl_errno = curl_errno($ch);
123
+ curl_close($ch);
124
+ $this->_handleError($curl_errno, $curl_error);
125
+ return false;
126
+ } else {
127
+ curl_close($ch);
128
+ if (trim($response) == "1") {
129
+ return true;
130
+ } else {
131
+ $this->_handleError(0, $response);
132
+ return false;
133
+ }
134
+ }
135
+ }
136
+
137
+
138
+ /**
139
+ * Write using a forked cURL process
140
+ * @param $url
141
+ * @param $data
142
+ * @return bool
143
+ */
144
+ protected function _execute_forked($url, $data) {
145
+
146
+ if ($this->_debug()) {
147
+ $this->_log("Making forked cURL call to $url");
148
+ }
149
+
150
+ $exec = 'curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d ' . $data . ' "' . $url . '"';
151
+
152
+ if(!$this->_debug()) {
153
+ $exec .= " >/dev/null 2>&1 &";
154
+ }
155
+
156
+ exec($exec, $output, $return_var);
157
+
158
+ if ($return_var != 0) {
159
+ $this->_handleError($return_var, $output);
160
+ }
161
+
162
+ return $return_var == 0;
163
+ }
164
+
165
+ /**
166
+ * @return int
167
+ */
168
+ public function getConnectTimeout()
169
+ {
170
+ return $this->_connect_timeout;
171
+ }
172
+
173
+ /**
174
+ * @return string
175
+ */
176
+ public function getEndpoint()
177
+ {
178
+ return $this->_endpoint;
179
+ }
180
+
181
+ /**
182
+ * @return bool|null
183
+ */
184
+ public function getFork()
185
+ {
186
+ return $this->_fork;
187
+ }
188
+
189
+ /**
190
+ * @return string
191
+ */
192
+ public function getHost()
193
+ {
194
+ return $this->_host;
195
+ }
196
+
197
+ /**
198
+ * @return array
199
+ */
200
+ public function getOptions()
201
+ {
202
+ return $this->_options;
203
+ }
204
+
205
+ /**
206
+ * @return string
207
+ */
208
+ public function getProtocol()
209
+ {
210
+ return $this->_protocol;
211
+ }
212
+
213
+ /**
214
+ * @return int
215
+ */
216
+ public function getTimeout()
217
+ {
218
+ return $this->_timeout;
219
+ }
220
+
221
+
222
+ }
lib/mixpanel/ConsumerStrategies/LI_FileConsumer.php ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(dirname(__FILE__) . "/LI_AbstractConsumer.php");
4
+ /**
5
+ * Consumes messages and writes them to a file
6
+ */
7
+ class ConsumerStrategies_LI_FileConsumer extends LI_ConsumerStrategies_AbstractConsumer {
8
+
9
+ /**
10
+ * @var string path to a file that we want to write the messages to
11
+ */
12
+ private $_file;
13
+
14
+
15
+ /**
16
+ * Creates a new LI_FileConsumer and assigns properties from the $options array
17
+ * @param array $options
18
+ */
19
+ function __construct($options) {
20
+ parent::__construct($options);
21
+
22
+ // what file to write to?
23
+ $this->_file = array_key_exists("file", $options) ? $options['file'] : dirname(__FILE__)."/../../messages.txt";
24
+ }
25
+
26
+
27
+ /**
28
+ * Append $batch to a file
29
+ * @param array $batch
30
+ * @return bool
31
+ */
32
+ public function persist($batch) {
33
+ if (count($batch) > 0) {
34
+ return file_put_contents($this->_file, json_encode($batch)."\n", FILE_APPEND | LOCK_EX) !== false;
35
+ } else {
36
+ return true;
37
+ }
38
+ }
39
+ }
lib/mixpanel/ConsumerStrategies/LI_SocketConsumer.php ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Portions of this class were borrowed from
4
+ * https://github.com/segmentio/analytics-php/blob/master/lib/Analytics/Consumer/Socket.php.
5
+ * Thanks for the work!
6
+ *
7
+ * WWWWWW||WWWWWW
8
+ * W W W||W W W
9
+ * ||
10
+ * ( OO )__________
11
+ * / | \
12
+ * /o o| MIT \
13
+ * \___/||_||__||_|| *
14
+ * || || || ||
15
+ * _||_|| _||_||
16
+ * (__|__|(__|__|
17
+ * (The MIT License)
18
+ *
19
+ * Copyright (c) 2013 Segment.io Inc. friends@segment.io
20
+ *
21
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
22
+ * documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the
23
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
24
+ * permit persons to whom the Software is furnished to do so, subject to the following conditions:
25
+ *
26
+ * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
27
+ * Software.
28
+ *
29
+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
30
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
31
+ * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
32
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
+ */
34
+
35
+ require_once(dirname(__FILE__) . "/LI_AbstractConsumer.php");
36
+
37
+ /**
38
+ * Consumes messages and writes them to host/endpoint using a persistent socket
39
+ */
40
+ class LI_ConsumerStrategies_SocketConsumer extends LI_ConsumerStrategies_AbstractConsumer {
41
+
42
+ /**
43
+ * @var string the host to connect to (e.g. api.mixpanel.com)
44
+ */
45
+ private $_host;
46
+
47
+
48
+ /**
49
+ * @var string the host-relative endpoint to write to (e.g. /engage)
50
+ */
51
+ private $_endpoint;
52
+
53
+
54
+ /**
55
+ * @var int connect_timeout the socket connection timeout in seconds
56
+ */
57
+ private $_connect_timeout;
58
+
59
+
60
+ /**
61
+ * @var string the protocol to use for the socket connection
62
+ */
63
+ private $_protocol;
64
+
65
+
66
+ /**
67
+ * @var resource holds the socket resource
68
+ */
69
+ private $_socket;
70
+
71
+ /**
72
+ * @var bool whether or not to wait for a response
73
+ */
74
+ private $_async;
75
+
76
+
77
+ /**
78
+ * Creates a new SocketConsumer and assigns properties from the $options array
79
+ * @param array $options
80
+ */
81
+ public function __construct($options = array()) {
82
+ parent::__construct($options);
83
+
84
+
85
+ $this->_host = $options['host'];
86
+ $this->_endpoint = $options['endpoint'];
87
+ $this->_connect_timeout = array_key_exists('connect_timeout', $options) ? $options['connect_timeout'] : 5;
88
+ $this->_async = array_key_exists('async', $options) && $options['async'] === false ? false : true;
89
+
90
+ if (array_key_exists('use_ssl', $options) && $options['use_ssl'] == true) {
91
+ $this->_protocol = "ssl";
92
+ $this->_port = 443;
93
+ } else {
94
+ $this->_protocol = "tcp";
95
+ $this->_port = 80;
96
+ }
97
+ }
98
+
99
+
100
+ /**
101
+ * Write using a persistent socket connection.
102
+ * @param array $batch
103
+ * @return bool
104
+ */
105
+ public function persist($batch) {
106
+
107
+ $socket = $this->_getSocket();
108
+ if (!is_resource($socket)) {
109
+ return false;
110
+ }
111
+
112
+ $data = "data=".$this->_encode($batch);
113
+
114
+ $body = "";
115
+ $body.= "POST ".$this->_endpoint." HTTP/1.1\r\n";
116
+ $body.= "Host: " . $this->_host . "\r\n";
117
+ $body.= "Content-Type: application/x-www-form-urlencoded\r\n";
118
+ $body.= "Accept: application/json\r\n";
119
+ $body.= "Content-length: " . strlen($data) . "\r\n";
120
+ $body.= "\r\n";
121
+ $body.= $data;
122
+
123
+ return $this->_write($socket, $body);
124
+ }
125
+
126
+
127
+ /**
128
+ * Return cached socket if open or create a new persistent socket
129
+ * @return bool|resource
130
+ */
131
+ private function _getSocket() {
132
+ if(is_resource($this->_socket)) {
133
+
134
+ if ($this->_debug()) {
135
+ $this->_log("Using existing socket");
136
+ }
137
+
138
+ return $this->_socket;
139
+ } else {
140
+
141
+ if ($this->_debug()) {
142
+ $this->_log("Creating new socket at ".time());
143
+ }
144
+
145
+ return $this->_createSocket();
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Attempt to open a new socket connection, cache it, and return the resource
151
+ * @param bool $retry
152
+ * @return bool|resource
153
+ */
154
+ private function _createSocket($retry = true) {
155
+ try {
156
+ $socket = pfsockopen($this->_protocol . "://" . $this->_host, $this->_port, $err_no, $err_msg, $this->_connect_timeout);
157
+
158
+ if ($this->_debug()) {
159
+ $this->_log("Opening socket connection to " . $this->_protocol . "://" . $this->_host . ":" . $this->_port);
160
+ }
161
+
162
+ if ($err_no != 0) {
163
+ $this->_handleError($err_no, $err_msg);
164
+ return $retry == true ? $this->_createSocket(false) : false;
165
+ } else {
166
+ // cache the socket
167
+ $this->_socket = $socket;
168
+ return $socket;
169
+ }
170
+
171
+ } catch (Exception $e) {
172
+ $this->_handleError($e->getCode(), $e->getMessage());
173
+ return $retry == true ? $this->_createSocket(false) : false;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Attempt to close and dereference a socket resource
179
+ */
180
+ private function _destroySocket() {
181
+ $socket = $this->_socket;
182
+ $this->_socket = null;
183
+ fclose($socket);
184
+ }
185
+
186
+
187
+ /**
188
+ * Write $data through the given $socket
189
+ * @param $socket
190
+ * @param $data
191
+ * @param bool $retry
192
+ * @return bool
193
+ */
194
+ private function _write($socket, $data, $retry = true) {
195
+
196
+ $bytes_sent = 0;
197
+ $bytes_total = strlen($data);
198
+ $socket_closed = false;
199
+ $success = true;
200
+ $max_bytes_per_write = 8192;
201
+
202
+ // if we have no data to write just return true
203
+ if ($bytes_total == 0) {
204
+ return true;
205
+ }
206
+
207
+ // try to write the data
208
+ while (!$socket_closed && $bytes_sent < $bytes_total) {
209
+
210
+ try {
211
+ $bytes = fwrite($socket, $data, $max_bytes_per_write);
212
+
213
+ if ($this->_debug()) {
214
+ $this->_log("Socket wrote ".$bytes." bytes");
215
+ }
216
+
217
+ // if we actually wrote data, then remove the written portion from $data left to write
218
+ if ($bytes > 0) {
219
+ $data = substr($data, $max_bytes_per_write);
220
+ }
221
+
222
+ } catch (Exception $e) {
223
+ $this->_handleError($e->getCode(), $e->getMessage());
224
+ $socket_closed = true;
225
+ }
226
+
227
+ if (isset($bytes) && $bytes) {
228
+ $bytes_sent += $bytes;
229
+ } else {
230
+ $socket_closed = true;
231
+ }
232
+ }
233
+
234
+ // create a new socket if the current one is closed and retry the message
235
+ if ($socket_closed) {
236
+
237
+ $this->_destroySocket();
238
+
239
+ if ($retry) {
240
+ if ($this->_debug()) {
241
+ $this->_log("Retrying socket write...");
242
+ }
243
+ $socket = $this->_getSocket();
244
+ if ($socket) return $this->_write($socket, $data, false);
245
+ }
246
+
247
+ return false;
248
+ }
249
+
250
+
251
+ // only wait for the response in debug mode or if we explicitly want to be synchronous
252
+ if ($this->_debug() || !$this->_async) {
253
+ $res = $this->handleResponse(fread($socket, 2048));
254
+ if ($res["status"] != "200") {
255
+ $this->_handleError($res["status"], $res["body"]);
256
+ $success = false;
257
+ }
258
+ }
259
+
260
+ return $success;
261
+ }
262
+
263
+
264
+ /**
265
+ * Parse the response from a socket write (only used for debugging)
266
+ * @param $response
267
+ * @return array
268
+ */
269
+ private function handleResponse($response) {
270
+
271
+ $lines = explode("\n", $response);
272
+
273
+ // extract headers
274
+ $headers = array();
275
+ foreach($lines as $line) {
276
+ $kvsplit = explode(":", $line);
277
+ if (count($kvsplit) == 2) {
278
+ $header = $kvsplit[0];
279
+ $value = $kvsplit[1];
280
+ $headers[$header] = trim($value);
281
+ }
282
+
283
+ }
284
+
285
+ // extract status
286
+ $line_one_exploded = explode(" ", $lines[0]);
287
+ $status = $line_one_exploded[1];
288
+
289
+ // extract body
290
+ $body = $lines[count($lines) - 1];
291
+
292
+ // if the connection has been closed lets kill the socket
293
+ if ($headers['Connection'] == "close") {
294
+ $this->_destroySocket();
295
+ if ($this->_debug()) {
296
+ $this->_log("Server told us connection closed so lets destroy the socket so it'll reconnect on next call");
297
+ }
298
+ }
299
+
300
+ $ret = array(
301
+ "status" => $status,
302
+ "body" => $body,
303
+ );
304
+
305
+ return $ret;
306
+ }
307
+
308
+
309
+ }
lib/mixpanel/ConsumerStrategies/SocketConsumer.php CHANGED
@@ -31,6 +31,8 @@
31
  * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
32
  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
  */
 
 
34
  require_once(dirname(__FILE__) . "/AbstractConsumer.php");
35
 
36
  /**
31
  * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
32
  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
  */
34
+ namespace LeadIn;
35
+
36
  require_once(dirname(__FILE__) . "/AbstractConsumer.php");
37
 
38
  /**
lib/mixpanel/{Mixpanel.php → LI_Mixpanel.php} RENAMED
@@ -1,8 +1,8 @@
1
  <?php
2
 
3
- require_once(dirname(__FILE__) . "/Base/MixpanelBase.php");
4
- require_once(dirname(__FILE__) . "/Producers/MixpanelPeople.php");
5
- require_once(dirname(__FILE__) . "/Producers/MixpanelEvents.php");
6
 
7
  /**
8
  * This is the main class for the Mixpanel PHP Library which provides all of the methods you need to track events and
@@ -14,7 +14,7 @@ require_once(dirname(__FILE__) . "/Producers/MixpanelEvents.php");
14
  * This library is built such that all messages are buffered in an in-memory "queue"
15
  * The queue will be automatically flushed at the end of every request. Alternatively, you can call "flush()" manually
16
  * at any time. Flushed messages will be passed to a Consumer's "persist" method. The library comes with a handful of
17
- * Consumers. The "CurlConsumer" is used by default which will send the messages to Mixpanel using forked cURL processes.
18
  * You can implement your own custom Consumer to customize how a message is sent to Mixpanel. This can be useful when
19
  * you want to put messages onto a distributed queue (such as ActiveMQ or Kestrel) instead of writing to Mixpanel in
20
  * the user thread.
@@ -75,12 +75,12 @@ require_once(dirname(__FILE__) . "/Producers/MixpanelEvents.php");
75
  * </tr>
76
  * <tr>
77
  * <td>connect_timeout</td>
78
- * <td>In both the SocketConsumer and CurlConsumer, this is used for the connection timeout (i.e. How long it has take to actually make a connection).
79
  * <td>5</td>
80
  * </tr>
81
  * <tr>
82
  * <td>timeout</td>
83
- * <td>In the CurlConsumer (non-forked), it is used to determine how long the cURL call has to execute.
84
  * <td>30</td>
85
  * </tr>
86
  * </table>
@@ -106,7 +106,7 @@ require_once(dirname(__FILE__) . "/Producers/MixpanelEvents.php");
106
  * ));
107
  *
108
  */
109
- class Mixpanel extends Base_MixpanelBase {
110
 
111
 
112
  /**
@@ -127,7 +127,7 @@ class Mixpanel extends Base_MixpanelBase {
127
  * An instance of the Mixpanel class (for singleton use)
128
  * @var Mixpanel
129
  */
130
- private static $_instance;
131
 
132
 
133
  /**
@@ -137,8 +137,8 @@ class Mixpanel extends Base_MixpanelBase {
137
  */
138
  public function __construct($token, $options = array()) {
139
  parent::__construct($options);
140
- $this->people = new Producers_MixpanelPeople($token, $options);
141
- $this->_events = new Producers_MixpanelEvents($token, $options);
142
  }
143
 
144
 
@@ -149,10 +149,13 @@ class Mixpanel extends Base_MixpanelBase {
149
  * @return Mixpanel
150
  */
151
  public static function getInstance($token, $options = array()) {
152
- if(!isset(self::$_instance)) {
153
- self::$_instance = new Mixpanel($token, $options);
154
- }
155
- return self::$_instance;
 
 
 
156
  }
157
 
158
 
1
  <?php
2
 
3
+ require_once(dirname(__FILE__) . "/Base/LI_MixpanelBase.php");
4
+ require_once(dirname(__FILE__) . "/Producers/LI_MixpanelPeople.php");
5
+ require_once(dirname(__FILE__) . "/Producers/LI_MixpanelEvents.php");
6
 
7
  /**
8
  * This is the main class for the Mixpanel PHP Library which provides all of the methods you need to track events and
14
  * This library is built such that all messages are buffered in an in-memory "queue"
15
  * The queue will be automatically flushed at the end of every request. Alternatively, you can call "flush()" manually
16
  * at any time. Flushed messages will be passed to a Consumer's "persist" method. The library comes with a handful of
17
+ * Consumers. The "LI_CurlConsumer" is used by default which will send the messages to Mixpanel using forked cURL processes.
18
  * You can implement your own custom Consumer to customize how a message is sent to Mixpanel. This can be useful when
19
  * you want to put messages onto a distributed queue (such as ActiveMQ or Kestrel) instead of writing to Mixpanel in
20
  * the user thread.
75
  * </tr>
76
  * <tr>
77
  * <td>connect_timeout</td>
78
+ * <td>In both the SocketConsumer and LI_CurlConsumer, this is used for the connection timeout (i.e. How long it has take to actually make a connection).
79
  * <td>5</td>
80
  * </tr>
81
  * <tr>
82
  * <td>timeout</td>
83
+ * <td>In the LI_CurlConsumer (non-forked), it is used to determine how long the cURL call has to execute.
84
  * <td>30</td>
85
  * </tr>
86
  * </table>
106
  * ));
107
  *
108
  */
109
+ class LI_Mixpanel extends LI_Base_MixpanelBase {
110
 
111
 
112
  /**
127
  * An instance of the Mixpanel class (for singleton use)
128
  * @var Mixpanel
129
  */
130
+ public $instance;
131
 
132
 
133
  /**
137
  */
138
  public function __construct($token, $options = array()) {
139
  parent::__construct($options);
140
+ $this->people = new LI_Producers_MixpanelPeople($token, $options);
141
+ $this->_events = new LI_Producers_MixpanelEvents($token, $options);
142
  }
143
 
144
 
149
  * @return Mixpanel
150
  */
151
  public static function getInstance($token, $options = array()) {
152
+
153
+
154
+
155
+
156
+ $this->instance = new LI_Mixpanel($token, $options);
157
+
158
+
159
  }
160
 
161
 
lib/mixpanel/Producers/LI_MixpanelBaseProducer.php ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(dirname(__FILE__) . "/../Base/LI_MixpanelBase.php");
4
+ require_once(dirname(__FILE__) . "/../ConsumerStrategies/LI_FileConsumer.php");
5
+ require_once(dirname(__FILE__) . "/../ConsumerStrategies/LI_CurlConsumer.php");
6
+ require_once(dirname(__FILE__) . "/../ConsumerStrategies/LI_SocketConsumer.php");
7
+
8
+ if (!function_exists('json_encode')) {
9
+ throw new Exception('The JSON PHP extension is required.');
10
+ }
11
+
12
+ /**
13
+ * Provides some base methods for use by a message Producer
14
+ */
15
+ abstract class LI_Producers_MixpanelBaseProducer extends LI_Base_MixpanelBase {
16
+
17
+
18
+ /**
19
+ * @var string a token associated to a Mixpanel project
20
+ */
21
+ protected $_token;
22
+
23
+
24
+ /**
25
+ * @var array a queue to hold messages in memory before flushing in batches
26
+ */
27
+ private $_queue = array();
28
+
29
+
30
+ /**
31
+ * @var LI_ConsumerStrategies_AbstractConsumer the consumer to use when flushing messages
32
+ */
33
+ private $_consumer = null;
34
+
35
+
36
+ /**
37
+ * @var array The list of available consumers
38
+ */
39
+ private $_consumers = array(
40
+ "file" => "LI_ConsumerStrategies_FileConsumer",
41
+ "curl" => "LI_ConsumerStrategies_CurlConsumer",
42
+ "socket" => "LI_ConsumerStrategies_SocketConsumer"
43
+ );
44
+
45
+
46
+ /**
47
+ * If the queue reaches this size we'll auto-flush to prevent out of memory errors
48
+ * @var int
49
+ */
50
+ protected $_max_queue_size = 1000;
51
+
52
+
53
+ /**
54
+ * Creates a new MixpanelBaseProducer, assings Mixpanel project token, registers custom Consumers, and instantiates
55
+ * the desired consumer
56
+ * @param $token
57
+ * @param array $options
58
+ */
59
+ public function __construct($token, $options = array()) {
60
+
61
+ parent::__construct($options);
62
+
63
+ // register any customer consumers
64
+ if (array_key_exists("consumers", $options)) {
65
+ $this->_consumers = array_merge($this->_consumers, $options['consumers']);
66
+ }
67
+
68
+ // set max queue size
69
+ if (array_key_exists("max_queue_size", $options)) {
70
+ $this->_max_queue_size = $options['max_queue_size'];
71
+ }
72
+
73
+ // associate token
74
+ $this->_token = $token;
75
+
76
+ if ($this->_debug()) {
77
+ $this->_log("Using token: ".$this->_token);
78
+ }
79
+
80
+ // instantiate the chosen consumer
81
+ $this->_consumer = $this->_getConsumer();
82
+
83
+ }
84
+
85
+
86
+ /**
87
+ * Flush the queue when we destruct the client with retries
88
+ */
89
+ public function __destruct() {
90
+ $attempts = 0;
91
+ $max_attempts = 10;
92
+ $success = false;
93
+ while (!$success && $attempts < $max_attempts) {
94
+ if ($this->_debug()) {
95
+ $this->_log("destruct flush attempt #".($attempts+1));
96
+ }
97
+ $success = $this->flush();
98
+ $attempts++;
99
+ }
100
+ }
101
+
102
+
103
+ /**
104
+ * Iterate the queue and write in batches using the instantiated Consumer Strategy
105
+ * @param int $desired_batch_size
106
+ * @return bool whether or not the flush was successful
107
+ */
108
+ public function flush($desired_batch_size = 50) {
109
+ $queue_size = count($this->_queue);
110
+ $succeeded = true;
111
+ if ($this->_debug()) {
112
+ $this->_log("Flush called - queue size: ".$queue_size);
113
+ }
114
+
115
+ while($queue_size > 0 && $succeeded) {
116
+ $batch_size = min(array($queue_size, $desired_batch_size, $this->_options['max_batch_size']));
117
+ $batch = array_splice($this->_queue, 0, $batch_size);
118
+ $succeeded = $this->_persist($batch);
119
+
120
+ if (!$succeeded) {
121
+ if ($this->_debug()) {
122
+ $this->_log("Batch consumption failed!");
123
+ }
124
+ $this->_queue = array_merge($batch, $this->_queue);
125
+
126
+ if ($this->_debug()) {
127
+ $this->_log("added batch back to queue, queue size is now $queue_size");
128
+ }
129
+ }
130
+
131
+ $queue_size = count($this->_queue);
132
+
133
+ if ($this->_debug()) {
134
+ $this->_log("Batch of $batch_size consumed, queue size is now $queue_size");
135
+ }
136
+ }
137
+ return $succeeded;
138
+ }
139
+
140
+
141
+ /**
142
+ * Empties the queue without persisting any of the messages
143
+ */
144
+ public function reset() {
145
+ $this->_queue = array();
146
+ }
147
+
148
+
149
+ /**
150
+ * Returns the in-memory queue
151
+ * @return array
152
+ */
153
+ public function getQueue() {
154
+ return $this->_queue;
155
+ }
156
+
157
+ /**
158
+ * Returns the current Mixpanel project token
159
+ * @return string
160
+ */
161
+ public function getToken() {
162
+ return $this->_token;
163
+ }
164
+
165
+
166
+ /**
167
+ * Given a strategy type, return a new PersistenceStrategy object
168
+ * @return LI_ConsumerStrategies_AbstractConsumer
169
+ */
170
+ protected function _getConsumer() {
171
+ $key = $this->_options['consumer'];
172
+ $Strategy = $this->_consumers[$key];
173
+ if ($this->_debug()) {
174
+ $this->_log("Using consumer: " . $key . " -> " . $Strategy);
175
+ }
176
+ $this->_options['endpoint'] = $this->_getEndpoint();
177
+
178
+ return new $Strategy($this->_options);
179
+ }
180
+
181
+
182
+ /**
183
+ * Add an array representing a message to be sent to Mixpanel to a queue.
184
+ * @param array $message
185
+ */
186
+ public function enqueue($message = array()) {
187
+ array_push($this->_queue, $message);
188
+
189
+ // force a flush if we've reached our threshold
190
+ if (count($this->_queue) > $this->_max_queue_size) {
191
+ $this->flush();
192
+ }
193
+
194
+ if ($this->_debug()) {
195
+ $this->_log("Queued message: ".json_encode($message));
196
+ }
197
+ }
198
+
199
+
200
+ /**
201
+ * Add an array representing a list of messages to be sent to Mixpanel to a queue.
202
+ * @param array $messages
203
+ */
204
+ public function enqueueAll($messages = array()) {
205
+ foreach($messages as $message) {
206
+ $this->enqueue($message);
207
+ }
208
+
209
+ }
210
+
211
+
212
+ /**
213
+ * Given an array of messages, persist it with the instantiated Persistence Strategy
214
+ * @param $message
215
+ * @return mixed
216
+ */
217
+ protected function _persist($message) {
218
+ return $this->_consumer->persist($message);
219
+ }
220
+
221
+
222
+
223
+
224
+ /**
225
+ * Return the endpoint that should be used by a consumer that consumes messages produced by this producer.
226
+ * @return string
227
+ */
228
+ abstract function _getEndpoint();
229
+
230
+ }
lib/mixpanel/Producers/LI_MixpanelEvents.php ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(dirname(__FILE__) . "/LI_MixpanelBaseProducer.php");
4
+ require_once(dirname(__FILE__) . "/LI_MixpanelPeople.php");
5
+ require_once(dirname(__FILE__) . "/../ConsumerStrategies/LI_CurlConsumer.php");
6
+
7
+ /**
8
+ * Provides an API to track events on Mixpanel
9
+ */
10
+ class LI_Producers_MixpanelEvents extends LI_Producers_MixpanelBaseProducer {
11
+
12
+ /**
13
+ * An array of properties to attach to every tracked event
14
+ * @var array
15
+ */
16
+ private $_super_properties = array("mp_lib" => "php");
17
+
18
+
19
+ /**
20
+ * Track an event defined by $event associated with metadata defined by $properties
21
+ * @param string $event
22
+ * @param array $properties
23
+ */
24
+ public function track($event, $properties = array()) {
25
+
26
+ // if no token is passed in, use current token
27
+ if (!array_key_exists("token", $properties)) $properties['token'] = $this->_token;
28
+
29
+ // if no time is passed in, use the current time
30
+ if (!array_key_exists('time', $properties)) $properties['time'] = time();
31
+
32
+ $params['event'] = $event;
33
+ $params['properties'] = array_merge($this->_super_properties, $properties);
34
+
35
+ $this->enqueue($params);
36
+ }
37
+
38
+
39
+ /**
40
+ * Register a property to be sent with every event. If the property has already been registered, it will be
41
+ * overwritten.
42
+ * @param string $property
43
+ * @param mixed $value
44
+ */
45
+ public function register($property, $value) {
46
+ $this->_super_properties[$property] = $value;
47
+ }
48
+
49
+
50
+ /**
51
+ * Register multiple properties to be sent with every event. If any of the properties have already been registered,
52
+ * they will be overwritten.
53
+ * @param array $props_and_vals
54
+ */
55
+ public function registerAll($props_and_vals = array()) {
56
+ foreach($props_and_vals as $property => $value) {
57
+ $this->register($property, $value);
58
+ }
59
+ }
60
+
61
+
62
+ /**
63
+ * Register a property to be sent with every event. If the property has already been registered, it will NOT be
64
+ * overwritten.
65
+ * @param $property
66
+ * @param $value
67
+ */
68
+ public function registerOnce($property, $value) {
69
+ if (!isset($this->_super_properties[$property])) {
70
+ $this->register($property, $value);
71
+ }
72
+ }
73
+
74
+
75
+ /**
76
+ * Register multiple properties to be sent with every event. If any of the properties have already been registered,
77
+ * they will NOT be overwritten.
78
+ * @param array $props_and_vals
79
+ */
80
+ public function registerAllOnce($props_and_vals = array()) {
81
+ foreach($props_and_vals as $property => $value) {
82
+ if (!isset($this->_super_properties[$property])) {
83
+ $this->register($property, $value);
84
+ }
85
+ }
86
+ }
87
+
88
+
89
+ /**
90
+ * Un-register an property to be sent with every event.
91
+ * @param string $property
92
+ */
93
+ public function unregister($property) {
94
+ unset($this->_super_properties[$property]);
95
+ }
96
+
97
+
98
+ /**
99
+ * Un-register a list of properties to be sent with every event.
100
+ * @param array $properties
101
+ */
102
+ public function unregisterAll($properties) {
103
+ foreach($properties as $property) {
104
+ $this->unregister($property);
105
+ }
106
+ }
107
+
108
+
109
+ /**
110
+ * Get a property that is set to be sent with every event
111
+ * @param string $property
112
+ * @return mixed
113
+ */
114
+ public function getProperty($property) {
115
+ return $this->_super_properties[$property];
116
+ }
117
+
118
+
119
+ /**
120
+ * Identify the user you want to associate to tracked events
121
+ * @param string|int $user_id
122
+ */
123
+ public function identify($user_id) {
124
+ $this->register("distinct_id", $user_id);
125
+ }
126
+
127
+
128
+ /**
129
+ * Alias an existing id with a different unique id. This is helpful when you want to associate a generated id to
130
+ * a username or e-mail address.
131
+ *
132
+ * Because aliasing can be extremely vulnerable to race conditions and ordering issues, we'll make a synchronous
133
+ * call directly to Mixpanel when this method is called. If it fails we'll throw an Exception as subsequent
134
+ * events are likely to be incorrectly tracked.
135
+ * @param string|int $original_id
136
+ * @param string|int $new_id
137
+ * @return array $msg
138
+ * @throws Exception
139
+ */
140
+ public function createAlias($original_id, $new_id) {
141
+ $msg = array(
142
+ "event" => '$create_alias',
143
+ "properties" => array("distinct_id" => $original_id, "alias" => $new_id, "token" => $this->_token)
144
+ );
145
+
146
+ $options = array_merge($this->_options, array("endpoint" => $this->_getEndpoint(), "fork" => false));
147
+ $LI_CurlConsumer = new LI_ConsumerStrategies_CurlConsumer($options);
148
+ $success = $LI_CurlConsumer->persist(array($msg));
149
+ if (!$success) {
150
+ //error_log("Creating Mixpanel Alias (original id: $original_id, new id: $new_id) failed");
151
+ //throw new Exception("Tried to create an alias but the call was not successful");
152
+ } else {
153
+ return $msg;
154
+ }
155
+ }
156
+
157
+
158
+ /**
159
+ * Returns the "events" endpoint
160
+ * @return string
161
+ */
162
+ function _getEndpoint() {
163
+ return $this->_options['events_endpoint'];
164
+ }
165
+ }
lib/mixpanel/Producers/LI_MixpanelPeople.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ require_once(dirname(__FILE__) . "/LI_MixpanelBaseProducer.php");
4
+
5
+ /**
6
+ * Provides an API to create/update profiles on Mixpanel
7
+ */
8
+ class LI_Producers_MixpanelPeople extends LI_Producers_MixpanelBaseProducer {
9
+
10
+ /**
11
+ * Internal method to prepare a message given the message data
12
+ * @param $distinct_id
13
+ * @param $operation
14
+ * @param $value
15
+ * @param null $ip
16
+ * @return array
17
+ */
18
+ private function _constructPayload($distinct_id, $operation, $value, $ip = null) {
19
+ $payload = array(
20
+ '$token' => $this->_token,
21
+ '$distinct_id' => $distinct_id,
22
+ $operation => $value
23
+ );
24
+ if ($ip !== null) $payload['$ip'] = $ip;
25
+ return $payload;
26
+ }
27
+
28
+ /**
29
+ * Set properties on a user record. If the profile does not exist, it creates it with these properties.
30
+ * If it does exist, it sets the properties to these values, overwriting existing values.
31
+ * @param string|int $distinct_id the distinct_id or alias of a user
32
+ * @param array $props associative array of properties to set on the profile
33
+ * @param string|null $ip the ip address of the client (used for geo-location)
34
+ */
35
+ public function set($distinct_id, $props, $ip = null) {
36
+ $payload = $this->_constructPayload($distinct_id, '$set', $props, $ip);
37
+ $this->enqueue($payload);
38
+ }
39
+
40
+ /**
41
+ * Set properties on a user record. If the profile does not exist, it creates it with these properties.
42
+ * If it does exist, it sets the properties to these values but WILL NOT overwrite existing values.
43
+ * @param string|int $distinct_id the distinct_id or alias of a user
44
+ * @param array $props associative array of properties to set on the profile
45
+ * @param string|null $ip the ip address of the client (used for geo-location)
46
+ */
47
+ public function setOnce($distinct_id, $props, $ip = null) {
48
+ $payload = $this->_constructPayload($distinct_id, '$set_once', $props, $ip);
49
+ $this->enqueue($payload);
50
+ }
51
+
52
+ /**
53
+ * Unset properties on a user record. If the profile does not exist, it creates it with no properties.
54
+ * If it does exist, it unsets these properties. NOTE: In other libraries we use 'unset' which is
55
+ * a reserved word in PHP.
56
+ * @param string|int $distinct_id the distinct_id or alias of a user
57
+ * @param array $props associative array of properties to unset on the profile
58
+ * @param string|null $ip the ip address of the client (used for geo-location)
59
+ */
60
+ public function remove($distinct_id, $props, $ip = null) {
61
+ $payload = $this->_constructPayload($distinct_id, '$unset', $props, $ip);
62
+ $this->enqueue($payload);
63
+ }
64
+
65
+ /**
66
+ * Increments the value of a property on a user record. If the profile does not exist, it creates it and sets the
67
+ * property to the increment value.
68
+ * @param string|int $distinct_id the distinct_id or alias of a user
69
+ * @param $prop string the property to increment
70
+ * @param int $val the amount to increment the property by
71
+ * @param string|null $ip the ip address of the client (used for geo-location)
72
+ */
73
+ public function increment($distinct_id, $prop, $val, $ip = null) {
74
+ $payload = $this->_constructPayload($distinct_id, '$add', array("$prop" => $val), $ip);
75
+ $this->enqueue($payload);
76
+ }
77
+
78
+ /**
79
+ * Adds $val to a list located at $prop. If the property does not exist, it will be created. If $val is a string
80
+ * and the list is empty or does not exist, a new list with one value will be created.
81
+ * @param string|int $distinct_id the distinct_id or alias of a user
82
+ * @param string $prop the property that holds the list
83
+ * @param string|array $val items to add to the list
84
+ * @param string|null $ip the ip address of the client (used for geo-location)
85
+ */
86
+ public function append($distinct_id, $prop, $val, $ip = null) {
87
+ $operation = gettype($val) == "array" ? '$union' : '$append';
88
+ $payload = $this->_constructPayload($distinct_id, $operation, array("$prop" => $val), $ip);
89
+ $this->enqueue($payload);
90
+ }
91
+
92
+ /**
93
+ * Adds a transaction to the user's profile for revenue tracking
94
+ * @param string|int $distinct_id the distinct_id or alias of a user
95
+ * @param string $amount the transaction amount e.g. "20.50"
96
+ * @param null $timestamp the timestamp of when the transaction occurred (default to current timestamp)
97
+ * @param string|null $ip the ip address of the client (used for geo-location)
98
+ */
99
+ public function trackCharge($distinct_id, $amount, $timestamp = null, $ip = null) {
100
+ $timestamp = $timestamp == null ? time() : $timestamp;
101
+ $date_iso = date("c", $timestamp);
102
+ $transaction = array(
103
+ '$time' => $date_iso,
104
+ '$amount' => $amount
105
+ );
106
+ $val = array('$transactions' => $transaction);
107
+ $payload = $this->_constructPayload($distinct_id, '$append', $val, $ip);
108
+ $this->enqueue($payload);
109
+ }
110
+
111
+ /**
112
+ * Clear all transactions stored on a user's profile
113
+ * @param string|int $distinct_id the distinct_id or alias of a user
114
+ * @param string|null $ip the ip address of the client (used for geo-location)
115
+ */
116
+ public function clearCharges($distinct_id, $ip = null) {
117
+ $payload = $this->_constructPayload($distinct_id, '$set', array('$transactions' => array()), $ip);
118
+ $this->enqueue($payload);
119
+ }
120
+
121
+ /**
122
+ * Delete this profile from Mixpanel
123
+ * @param string|int $distinct_id the distinct_id or alias of a user
124
+ * @param string|null $ip the ip address of the client (used for geo-location)
125
+ */
126
+ public function deleteUser($distinct_id, $ip = null) {
127
+ $payload = $this->_constructPayload($distinct_id, '$delete', "", $ip);
128
+ $this->enqueue($payload);
129
+ }
130
+
131
+ /**
132
+ * Returns the "engage" endpoint
133
+ * @return string
134
+ */
135
+ function _getEndpoint() {
136
+ return $this->_options['people_endpoint'];
137
+ }
138
+
139
+ }
lib/mixpanel/Producers/MixpanelBaseProducer.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
  require_once(dirname(__FILE__) . "/../Base/MixpanelBase.php");
3
  require_once(dirname(__FILE__) . "/../ConsumerStrategies/FileConsumer.php");
4
  require_once(dirname(__FILE__) . "/../ConsumerStrategies/CurlConsumer.php");
@@ -36,9 +38,9 @@ abstract class Producers_MixpanelBaseProducer extends Base_MixpanelBase {
36
  * @var array The list of available consumers
37
  */
38
  private $_consumers = array(
39
- "file" => "ConsumerStrategies_FileConsumer",
40
- "curl" => "ConsumerStrategies_CurlConsumer",
41
- "socket" => "ConsumerStrategies_SocketConsumer"
42
  );
43
 
44
 
1
  <?php
2
+ namespace LeadIn;
3
+
4
  require_once(dirname(__FILE__) . "/../Base/MixpanelBase.php");
5
  require_once(dirname(__FILE__) . "/../ConsumerStrategies/FileConsumer.php");
6
  require_once(dirname(__FILE__) . "/../ConsumerStrategies/CurlConsumer.php");
38
  * @var array The list of available consumers
39
  */
40
  private $_consumers = array(
41
+ "file" => "LeadIn\ConsumerStrategies_FileConsumer",
42
+ "curl" => "LeadIn\ConsumerStrategies_CurlConsumer",
43
+ "socket" => "LeadIn\ConsumerStrategies_SocketConsumer"
44
  );
45
 
46
 
lib/mixpanel/Producers/MixpanelEvents.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
  require_once(dirname(__FILE__) . "/MixpanelBaseProducer.php");
3
  require_once(dirname(__FILE__) . "/MixpanelPeople.php");
4
  require_once(dirname(__FILE__) . "/../ConsumerStrategies/CurlConsumer.php");
1
  <?php
2
+ namespace LeadIn;
3
+
4
  require_once(dirname(__FILE__) . "/MixpanelBaseProducer.php");
5
  require_once(dirname(__FILE__) . "/MixpanelPeople.php");
6
  require_once(dirname(__FILE__) . "/../ConsumerStrategies/CurlConsumer.php");
lib/mixpanel/Producers/MixpanelPeople.php CHANGED
@@ -1,4 +1,6 @@
1
  <?php
 
 
2
  require_once(dirname(__FILE__) . "/MixpanelBaseProducer.php");
3
 
4
  /**
1
  <?php
2
+ namespace LeadIn;
3
+
4
  require_once(dirname(__FILE__) . "/MixpanelBaseProducer.php");
5
 
6
  /**
power-ups/constant-contact-list-sync.php ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Power-up Name: Contact Sync
4
+ * Power-up Class: WPConstantContactListSync
5
+ * Power-up Menu Text:
6
+ * Power-up Slug: constant_contact_list_sync
7
+ * Power-up Menu Link: settings
8
+ * Power-up URI: http://leadin.com/constant-contact-list-sync
9
+ * Power-up Description: Sync your subscribers to a Constant Contact email list.
10
+ * Power-up Icon: power-up-icon-constant-contact-list-sync
11
+ * Power-up Icon Small: power-up-icon-constant-contact-list-sync_small
12
+ * First Introduced: 0.8.0
13
+ * Power-up Tags: Newsletter, Email
14
+ * Auto Activate: No
15
+ */
16
+
17
+ //=============================================
18
+ // Define Constants
19
+ //=============================================
20
+
21
+ if ( !defined('LEADIN_CONSTANT_CONTACT_LIST_SYNC_PATH') )
22
+ define('LEADIN_CONSTANT_CONTACT_LIST_SYNC_PATH', LEADIN_PATH . '/power-ups/constant-contact-list-sync');
23
+
24
+ if ( !defined('LEADIN_CONSTANT_CONTACT_LIST_SYNC_PLUGIN_DIR') )
25
+ define('LEADIN_CONSTANT_CONTACT_LIST_SYNC_PLUGIN_DIR', LEADIN_PLUGIN_DIR . '/power-ups/constant-contact-list-sync');
26
+
27
+ if ( !defined('LEADIN_CONSTANT_CONTACT_LIST_SYNC_PLUGIN_SLUG') )
28
+ define('LEADIN_CONSTANT_CONTACT_LIST_SYNC_SLUG', basename(dirname(__FILE__)));
29
+
30
+ if ( !defined('LEADIN_CONSTANT_CONTACT_API_KEY') )
31
+ define('LEADIN_CONSTANT_CONTACT_API_KEY', 'p5hrzdhe2zrwbm76r2u7pvtc');
32
+
33
+
34
+
35
+ //=============================================
36
+ // Include Needed Files
37
+ //=============================================
38
+ require_once(LEADIN_CONSTANT_CONTACT_LIST_SYNC_PLUGIN_DIR . '/admin/constant-contact-list-sync-admin.php');
39
+ require_once(LEADIN_CONSTANT_CONTACT_LIST_SYNC_PLUGIN_DIR . '/inc/li_constant_contact.php');
40
+
41
+ //=============================================
42
+ // WPLeadIn Class
43
+ //=============================================
44
+ class WPConstantContactListSync extends WPLeadIn {
45
+
46
+ var $admin;
47
+
48
+ /**
49
+ * Class constructor
50
+ */
51
+ function __construct ( $activated )
52
+ {
53
+ //=============================================
54
+ // Hooks & Filters
55
+ //=============================================
56
+
57
+ if ( ! $activated )
58
+ return false;
59
+
60
+ global $leadin_constant_contact_list_sync_wp;
61
+ $leadin_constant_contact_list_sync_wp = $this;
62
+
63
+ }
64
+
65
+ public function admin_init ( )
66
+ {
67
+ $admin_class = get_class($this) . 'Admin';
68
+ $this->admin = new $admin_class($this->icon_small);
69
+ }
70
+
71
+ function power_up_setup_callback ( )
72
+ {
73
+ $this->admin->power_up_setup_callback();
74
+ }
75
+
76
+ /**
77
+ * Activate the power-up
78
+ */
79
+ function add_leadin_subscribe_defaults ()
80
+ {
81
+ $lis_options = get_option('leadin_subscribe_options');
82
+
83
+ if ( ($lis_options['li_susbscibe_installed'] != 1) || (!is_array($lis_options)) )
84
+ {
85
+ $opt = array(
86
+ 'li_susbscibe_installed' => '1',
87
+ 'li_subscribe_heading' => 'Sign up for my newsletter to get new posts by email'
88
+ );
89
+
90
+ update_option('leadin_subscribe_options', $opt);
91
+ }
92
+ }
93
+
94
+ function push_constant_contact_subscriber_to_list ( $email = '', $first_name = '', $last_name = '', $phone = '' )
95
+ {
96
+ $options = get_option('leadin_cc_options');
97
+ $li_cc_subscribers_to_list = ( isset($options['li_cc_subscribers_to_list']) ? $options['li_cc_subscribers_to_list'] : '' );
98
+
99
+ if ( isset($options['li_cc_email']) && isset($options['li_cc_password']) && $options['li_cc_email'] && $options['li_cc_password'] && $li_cc_subscribers_to_list )
100
+ {
101
+ $this->constant_contact = new LI_ConstantContact($options['li_cc_email'], $options['li_cc_password'], LEADIN_CONSTANT_CONTACT_API_KEY, TRUE);
102
+
103
+ $contact = array();
104
+
105
+ if ( $email )
106
+ $contact['EmailAddress'] = $email;
107
+
108
+ if ( $first_name )
109
+ $contact['FirstName'] = $first_name;
110
+
111
+ if ( $last_name )
112
+ $contact['LastName'] = $last_name;
113
+
114
+ if ( $phone )
115
+ $contact['HomePhone'] = $phone;
116
+
117
+ if ( $phone )
118
+ $contact['WorkPhone'] = $phone;
119
+
120
+ $this->constant_contact->add_contact($contact, array($li_cc_subscribers_to_list));
121
+ }
122
+ }
123
+ }
124
+
125
+ //=============================================
126
+ // Subscribe Widget Init
127
+ //=============================================
128
+
129
+ global $leadin_constant_contact_list_sync_wp;
130
+
131
+ ?>
power-ups/constant-contact-list-sync/admin/constant-contact-list-sync-admin.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //=============================================
3
+ // WPLeadInAdmin Class
4
+ //=============================================
5
+ class WPConstantContactListSyncAdmin extends WPLeadInAdmin {
6
+
7
+ var $power_up_settings_section = 'leadin_cc_options_section';
8
+ var $power_up_icon;
9
+ var $auth_set;
10
+ var $bad_api_call;
11
+ var $constant_contact;
12
+ var $lists;
13
+
14
+ /**
15
+ * Class constructor
16
+ */
17
+ function __construct ( $small_icon )
18
+ {
19
+ //=============================================
20
+ // Hooks & Filters
21
+ //=============================================
22
+
23
+ if ( is_admin() )
24
+ {
25
+ $this->power_up_icon = '<img src="' . LEADIN_PATH . '/images/' . $small_icon . '.png" class="power-up-settings-icon"/>';
26
+ add_action('admin_init', array($this, 'leadin_cc_build_settings_page'));
27
+ }
28
+ }
29
+
30
+ //=============================================
31
+ // Settings Page
32
+ //=============================================
33
+
34
+ /**
35
+ * Creates settings options
36
+ */
37
+ function leadin_cc_build_settings_page ()
38
+ {
39
+ global $leadin_constant_contact_list_sync_wp;
40
+
41
+ $options = get_option('leadin_cc_options');
42
+
43
+ register_setting('leadin_settings_options', 'leadin_cc_options', array($this, 'sanitize'));
44
+
45
+ // If the creds are set, check if they are any good by hitting the API
46
+ if ( isset($options['li_cc_email']) && isset($options['li_cc_password']) && $options['li_cc_email'] && $options['li_cc_password'] )
47
+ {
48
+ $this->auth_set = TRUE;
49
+
50
+ // Try to make a request using the authentication credentials
51
+ $this->lists = $this->li_cc_get_email_lists(LEADIN_CONSTANT_CONTACT_API_KEY, $options['li_cc_email'], $options['li_cc_password']);
52
+
53
+ if ( $this->constant_contact->cc_exception )
54
+ {
55
+ $this->bad_api_call = TRUE;
56
+ }
57
+ }
58
+
59
+ add_settings_section($this->power_up_settings_section, $this->power_up_icon . "Constant Contact", array($this, 'cc_section_callback'), LEADIN_ADMIN_PATH);
60
+
61
+ if ( $this->auth_set && ! $this->bad_api_call )
62
+ {
63
+ add_settings_field('li_cc_subscribers_to_list', 'Add subscribers to list', array($this, 'li_cc_subscribers_to_list_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
64
+ }
65
+ else
66
+ {
67
+ add_settings_field('li_cc_email', 'Email', array($this, 'li_cc_email_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
68
+ add_settings_field('li_cc_password', 'Password', array($this, 'li_cc_password_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
69
+ }
70
+ }
71
+
72
+ function cc_section_callback ( )
73
+ {
74
+ if ( ! $this->auth_set )
75
+ echo 'Sign into your Constant Contact account below to setup Contact Sync';
76
+ else if ( $this->bad_api_call )
77
+ echo '<div id="message" class="updated below-h2 error"><p>' . $this->constant_contact->cc_exception . '</p></div>';
78
+
79
+ $this->print_hidden_settings_fields();
80
+ }
81
+
82
+ function print_hidden_settings_fields ()
83
+ {
84
+ // Hacky solution to solve the Settings API overwriting the default values
85
+ $options = get_option('leadin_cc_options');
86
+ $li_cc_email = ( $options['li_cc_email'] ? $options['li_cc_email'] : '' );
87
+ $li_cc_password = ( $options['li_cc_password'] ? $options['li_cc_password'] : '' );
88
+
89
+ if ( $li_cc_email )
90
+ {
91
+ printf(
92
+ '<input id="li_cc_email" type="hidden" name="leadin_cc_options[li_cc_email]" value="%s"/>',
93
+ $li_cc_email
94
+ );
95
+ }
96
+
97
+ if ( $li_cc_password )
98
+ {
99
+ printf(
100
+ '<input id="li_cc_password" type="hidden" name="leadin_cc_options[li_cc_password]" value="%s"/>',
101
+ $li_cc_password
102
+ );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Sanitize each setting field as needed
108
+ *
109
+ * @param array $input Contains all settings fields as array keys
110
+ */
111
+ public function sanitize ( $input )
112
+ {
113
+ $new_input = array();
114
+
115
+ if( isset( $input['li_cc_email'] ) )
116
+ $new_input['li_cc_email'] = sanitize_text_field( $input['li_cc_email'] );
117
+
118
+ if( isset( $input['li_cc_password'] ) )
119
+ $new_input['li_cc_password'] = sanitize_text_field( $input['li_cc_password'] );
120
+
121
+ if( isset( $input['li_cc_subscribers_to_list'] ) )
122
+ $new_input['li_cc_subscribers_to_list'] = sanitize_text_field( $input['li_cc_subscribers_to_list'] );
123
+
124
+ return $new_input;
125
+ }
126
+
127
+ /**
128
+ * Prints email input for settings page
129
+ */
130
+ function li_cc_email_callback ()
131
+ {
132
+ $options = get_option('leadin_cc_options');
133
+ $li_cc_email = ( $options['li_cc_email'] ? $options['li_cc_email'] : '' ); // Get header from options, or show default
134
+
135
+ printf(
136
+ '<input id="li_cc_email" type="text" id="title" name="leadin_cc_options[li_cc_email]" value="%s" size="50"/>',
137
+ $li_cc_email
138
+ );
139
+ }
140
+
141
+ /**
142
+ * Prints password input for settings page
143
+ */
144
+ function li_cc_password_callback ()
145
+ {
146
+ $options = get_option('leadin_cc_options');
147
+ $li_cc_password = ( $options['li_cc_password'] ? $options['li_cc_password'] : '' ); // Get header from options, or show default
148
+
149
+ printf(
150
+ '<input id="li_cc_password" type="password" id="title" name="leadin_cc_options[li_cc_password]" value="%s" size="50"/>',
151
+ $li_cc_password
152
+ );
153
+ }
154
+
155
+ /**
156
+ * Prints email input for settings page
157
+ */
158
+ function li_cc_subscribers_to_list_callback ()
159
+ {
160
+ $options = get_option('leadin_cc_options');
161
+ $li_cc_subscribers_to_list = ( isset($options['li_cc_subscribers_to_list']) ? $options['li_cc_subscribers_to_list'] : '' ); // Get header from options, or show default
162
+
163
+ echo '<select id="li_cc_subscribers_to_list" name="leadin_cc_options[li_cc_subscribers_to_list]" ' . ( ! count($this->lists) ? 'disabled' : '' ) . '>';
164
+
165
+ if ( count($this->lists) )
166
+ {
167
+ $list_set = FALSE;
168
+
169
+ foreach ( $this->lists as $list )
170
+ {
171
+ // Skip over default lists
172
+ if ( $list['Name'] == 'Active' || $list['Name'] == 'Do Not Mail' || $list['Name'] == 'Removed' )
173
+ continue;
174
+
175
+ if ( urldecode($list['ListID']) == $li_cc_subscribers_to_list && !$list_set )
176
+ $list_set = TRUE;
177
+
178
+ echo '<option ' . ( urldecode($list['ListID']) == $li_cc_subscribers_to_list ? 'selected' : '' ) . ' value="' . urldecode($list['ListID']) . '">' . $list['Name'] . '</option>';
179
+ }
180
+
181
+ if ( !$list_set )
182
+ echo '<option selected value="">No list set...</option>';
183
+ }
184
+ else
185
+ {
186
+ echo '<option value="No lists...">No lists...</option>';
187
+ }
188
+
189
+ echo '</select>';
190
+ echo '<p><a href="https://login.constantcontact.com/login/login.sdo?goto=https://ui.constantcontact.com/rnavmap/distui/contacts" target="_blank">Create a new list on ConstantContact.com</a></p>';
191
+ }
192
+
193
+ function li_cc_get_email_lists ( $api_key, $username, $password )
194
+ {
195
+ $this->constant_contact = new LI_ConstantContact($username, $password, $api_key, FALSE);
196
+ $lists = $this->constant_contact->get_lists();
197
+
198
+ if ( count($lists) )
199
+ return $lists;
200
+ else
201
+ return FALSE;
202
+ }
203
+ }
204
+
205
+ ?>
power-ups/constant-contact-list-sync/inc/li_constant_contact.php ADDED
@@ -0,0 +1,1114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ global $cc_exception;
4
+
5
+ class LI_ConstantContact
6
+ {
7
+ var $auth;
8
+ var $api;
9
+ var $debug;
10
+ var $cc_exception;
11
+
12
+ /**
13
+ * The ConstantContact contructor
14
+ *
15
+ * @param String $username - Your username
16
+ * @param String $password - Your password
17
+ * @param String $api_key - Your API Key
18
+ * @param Boolean $debug - Whether or not debugging output is displayed
19
+ * @param String $debug_style - Options are "cli" or "html". All it does is print "\n" or "<br>" for debugging output.
20
+ */
21
+
22
+ public function __construct ( $username = '', $password = '', $api_key = '', $debug_enabled = FALSE, $debug_style = 'cli' )
23
+ {
24
+ $this->debug['enabled'] = $debug_enabled;
25
+ $this->debug['style'] = $debug_style;
26
+ $this->debug['last_response'] = 0;
27
+ $this->auth['username'] = $username;
28
+ $this->auth['password'] = $password;
29
+ $this->auth['api_key'] = $api_key;
30
+ $this->api['url'] = 'https://api.constantcontact.com/ws/customers/'.$username.'/';
31
+ $this->api['inner_url'] = 'http://api.constantcontact.com/ws/customers/'.$username.'/';
32
+ $this->api['relative_url'] = '/ws/customers/'.$username.'/';
33
+ }
34
+
35
+ //Main Functions
36
+
37
+ /**
38
+ * Print debugging output
39
+ *
40
+ * @param String $string - String to print
41
+ */
42
+ private function debug_print($string)
43
+ {
44
+ if($this->debug['enabled'])
45
+ {
46
+ $line_end = "\n";
47
+ if ($this->debug['style'] == 'html')
48
+ $line_end = "<br>";
49
+ print "ConstantContact Debug: $string{$line_end}";
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Fetch a URI from ConstantContact
55
+ *
56
+ * @param String $url - URL to connect to
57
+ * @param String $post - POST data to include
58
+ * @param String $fetch_as - Either array or xml for the return object
59
+ * @param String $call_type - NORMAL,PUT,DELETE
60
+ * @return Respose
61
+ */
62
+ private function fetch($url,$post,$fetch_as='array',$call_type='NORMAL')
63
+ {
64
+ $this->debug_print("------------------ fetch ------------------");
65
+
66
+ $credentials=$this->auth['api_key'].'%'.$this->auth['username'].':'.$this->auth['password'];
67
+
68
+ $this->debug_print("Connecting to '".$url."'");
69
+ $this->debug_print("With Credentials '".$credentials."'");
70
+
71
+ $ch=curl_init();
72
+
73
+ curl_setopt($ch, CURLOPT_URL, $url);
74
+ curl_setopt($ch, CURLOPT_USERPWD, $credentials);
75
+ curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
76
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION ,1);
77
+ curl_setopt($ch, CURLOPT_TIMEOUT, 30);
78
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
79
+ if(strlen(trim($post))>0)
80
+ {
81
+ if ($this->debug['style'] == 'cli')
82
+ $this->debug_print("And Posting:\n\n---START---\n".$post."\n---END---\n\n");
83
+ elseif ($this->debug['style'] == 'html')
84
+ $this->debug_print("And Posting:<pre>\n\n---START---\n".htmlspecialchars($post)."\n---END---\n\n</pre>");
85
+ if ($call_type == 'PUT')
86
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
87
+ elseif ($call_type == 'DELETE')
88
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
89
+ else
90
+ curl_setopt($ch, CURLOPT_POST, 1);
91
+
92
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
93
+ curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/atom+xml'));
94
+ }
95
+ else
96
+ {
97
+ $this->debug_print("Not posting");
98
+ curl_setopt($ch, CURLOPT_POST, 0);
99
+ if ($call_type == 'PUT')
100
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
101
+ elseif ($call_type == 'DELETE')
102
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
103
+ }
104
+
105
+ curl_setopt($ch, CURLOPT_HEADER, 0);
106
+
107
+ $response = curl_exec($ch);
108
+ $response_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
109
+ $this->debug_print("HTTP Response Code '".$response_code."'");
110
+
111
+ if ( ! in_array($response_code, array('201', '200', '204')) )
112
+ {
113
+ if ( $response_code == 401 )
114
+ {
115
+ $this->cc_exception = "Sorry, that username and password combination isn't right";
116
+ }
117
+ else if ( $response_code == 403 )
118
+ {
119
+ $this->cc_exception = "Your account is locked due to too many bad logins. You can try <a href='https://login.constantcontact.com/login/pwreset.sdo' target='_blank'>resetting your Constant Contact password</a> to unlock your account";
120
+ }
121
+ }
122
+
123
+ $this->debug['last_response'] = $response_code;
124
+
125
+ if ($this->debug['style'] == 'cli')
126
+ $this->debug_print("Received:\n\n---START---\n".$response."\n---END---\n\n");
127
+ elseif ($this->debug['style'] == 'html')
128
+ $this->debug_print("Received:<pre>\n\n---START---\n".htmlspecialchars($response)."\n---END---\n\n</pre>");
129
+
130
+ curl_close($ch);
131
+ if ($fetch_as == 'array')
132
+ return $this->xml_to_array($response);
133
+ else
134
+ return $response;
135
+ }
136
+
137
+ /**
138
+ * Converts XML data into a nested array
139
+ *
140
+ * @param String $contents - XML string
141
+ * @param Integers $get_attributes - Include attributes (default 1)
142
+ * @return Nested associative array
143
+ */
144
+ public function xml_to_array($contents, $get_attributes=1)
145
+ {
146
+ if (!$contents)
147
+ return array();
148
+
149
+ if (!function_exists('xml_parser_create'))
150
+ {
151
+ $this->debug_print("ERROR: xml_parser_create function doesn't exist!");
152
+ return array();
153
+ }
154
+
155
+ $parser = xml_parser_create();
156
+ xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 0 );
157
+ xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 );
158
+ xml_parse_into_struct( $parser, $contents, $xml_values );
159
+ xml_parser_free( $parser );
160
+
161
+ if (!$xml_values)
162
+ {
163
+ $this->debug_print("WARN: Could not parse xml_values!");
164
+ return;
165
+ }
166
+
167
+ $xml_array = array();
168
+ $parents = array();
169
+ $opened_tags = array();
170
+ $arr = array();
171
+
172
+ $current = &$xml_array;
173
+
174
+ foreach( $xml_values as $data)
175
+ {
176
+ unset($attributes,$value);
177
+ extract($data);
178
+
179
+ $result = '';
180
+ if ($get_attributes)
181
+ {
182
+ $result = array();
183
+ if (isset($value))
184
+ $result['value'] = $value;
185
+ if (isset($attributes))
186
+ {
187
+ foreach ($attributes as $attr => $val)
188
+ {
189
+ if ($get_attributes == 1)
190
+ $result['attr'][$attr] = $val;
191
+ }
192
+ }
193
+ }
194
+ elseif (isset($value))
195
+ $result = $value;
196
+
197
+ if ($type == "open")
198
+ {
199
+ $parent[$level-1] = &$current;
200
+ if (!is_array($current) || !in_array($tag, array_keys($current)))
201
+ {
202
+ $current[$tag] = $result;
203
+ $current = &$current[$tag];
204
+ }
205
+ else
206
+ {
207
+ if (isset($current[$tag][0]))
208
+ array_push($current[$tag], $result);
209
+ else
210
+ $current[$tag] = array($current[$tag],$result);
211
+
212
+ $last = count($current[$tag]) - 1;
213
+ $current = &$current[$tag][$last];
214
+ }
215
+ }
216
+ elseif ($type == "complete")
217
+ {
218
+ if (!isset($current[$tag]))
219
+ $current[$tag] = $result;
220
+ else
221
+ {
222
+ if ((is_array($current[$tag]) and $get_attributes == 0) || (isset($current[$tag][0]) and is_array($current[$tag][0]) and $get_attributes == 1))
223
+ array_push($current[$tag],$result);
224
+ else
225
+ $current[$tag] = array($current[$tag],$result);
226
+ }
227
+ }
228
+ elseif ($type == 'close')
229
+ $current = &$parent[$level-1];
230
+ }
231
+ return($xml_array);
232
+ }
233
+
234
+ /**
235
+ * Converts id URL to numeric id
236
+ *
237
+ * @param String $url - ID URL
238
+ * @return Numeric ID
239
+ */
240
+ public function id_from_url($url)
241
+ {
242
+ $temp = Array();
243
+ $temp = explode('/',trim($url));
244
+ return trim($temp[count($temp)-1]);
245
+ }
246
+
247
+ /**
248
+ * Retrieves the XML service document
249
+ *
250
+ * @return XML service document
251
+ */
252
+ public function get_service_doc_xml()
253
+ {
254
+ $url=$this->api_url;
255
+ $post='';
256
+ $response=$this->fetch($url,$post,'xml');
257
+ return $response;
258
+ }
259
+
260
+
261
+ //Contact Functions
262
+
263
+ /**
264
+ * Formats a contact array into the format we always return as
265
+ * Note:
266
+ * I like to do this because the atom specification has so much extraneous data
267
+ * that I just don't care about. This makes a sane array out of what is
268
+ * incredibly overcomplicated in my opinion.
269
+ *
270
+ * @param Array - Contact array as returned from fetch/xml_to_array
271
+ * @return Associative array in the format returned by this api
272
+ */
273
+ public function format_contact($response)
274
+ {
275
+ $my_data = Array();
276
+ $my_data = $response['entry']['content']['Contact'];
277
+ $d = Array();
278
+ $d['ContactID'] = $this->id_from_url($my_data['attr']['id']);
279
+ unset($my_data['attr']);
280
+ foreach ($my_data as $dkey => $dval)
281
+ {
282
+ if ($dkey != 'ContactLists')
283
+ $d[$dkey] = $dval['value'];
284
+ else
285
+ {
286
+ $entries = Array();
287
+ if (isset($dval['ContactList']['link']))
288
+ $entries[] = $dval['ContactList'];
289
+ else
290
+ $entries = $dval['ContactList'];
291
+
292
+ foreach ($entries as $entry)
293
+ {
294
+ $e = Array();
295
+ $e['ListID'] = $this->id_from_url($entry['attr']['id']);
296
+ unset($entry['attr']);
297
+ unset($entry['link']);
298
+ foreach ($entry as $ekey => $eval)
299
+ $e[$ekey] = $eval['value'];
300
+ $d[$dkey][] = $e;
301
+ }
302
+ }
303
+ }
304
+ return $d;
305
+ }
306
+
307
+ /**
308
+ * Return your list of contacts.
309
+ *
310
+ * @param String $key - Optionally specify a key to use for the array
311
+ * @param Boolean $unique - Whether the key you specified is unique
312
+ * @return Associative array containing your contacts indexed by whatever key specified.
313
+ */
314
+ public function get_contacts($key='',$unique=true)
315
+ {
316
+ $ret = Array();
317
+
318
+ $url = $this->api['url'].'contacts';
319
+ $post = '';
320
+ $response = Array();
321
+ $response = $this->fetch($url,$post);
322
+
323
+ $entries = Array();
324
+ if (isset($response['feed']['entry']['link']))
325
+ $entries[] = $response['feed']['entry'];
326
+ else
327
+ $entries = $response['feed']['entry'];
328
+ foreach ($entries as $data)
329
+ {
330
+ $my_data = Array();
331
+ $my_data = $data['content']['Contact'];
332
+ $d = Array();
333
+ $d['ContactID'] = $this->id_from_url($my_data['attr']['id']);
334
+ unset($my_data['attr']);
335
+ foreach ($my_data as $dkey => $dval)
336
+ $d[$dkey] = $dval['value'];
337
+
338
+ if ($key)
339
+ {
340
+ if ($unique)
341
+ $ret[$d[$key]] = $d;
342
+ else
343
+ $ret[$d[$key]][] = $d;
344
+ }
345
+ else
346
+ $ret[] = $d;
347
+ }
348
+ return $ret;
349
+ }
350
+
351
+ /**
352
+ * Add a contact
353
+ * See Contact Data Format at http://developer.constantcontact.com/doc/contactCollection for all fields
354
+ *
355
+ * @param Array $data - An associative array containing all the contact data to add
356
+ * @param Array $lists - An array of list_ids to add this contact to
357
+ * @return Integer - ContactID for the new entry
358
+ */
359
+ public function add_contact($data,$lists)
360
+ {
361
+
362
+ $updated = date('Y-m-d\TH:i:s\Z');
363
+ $xml_string = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><entry xmlns='http://www.w3.org/2005/Atom'></entry>";
364
+ $xml_object = simplexml_load_string($xml_string);
365
+ $xml_object->addChild("title");
366
+ $xml_object->addChild("updated",$updated);
367
+ $author_node = $xml_object->addChild("author");
368
+ $author_node->addChild("name", ("CTCT Samples"));
369
+ $xml_object->addChild("id", 'data:,none');
370
+ $summary_node = $xml_object->addChild("summary");
371
+ $summary_node->addAttribute("type", "text");
372
+ $content_node = $xml_object->addChild("content");
373
+ $content_node->addAttribute("type", "application/vnd.ctct+xml");
374
+ $contact_node = $content_node->addChild("Contact");
375
+ $contact_node->addAttribute("xmlns", "http://ws.constantcontact.com/ns/1.0/");
376
+ $contact_node->addChild("OptInSource", 'ACTION_BY_CUSTOMER');
377
+
378
+ foreach ( $data as $key => $val )
379
+ {
380
+ $contact_node->addChild($key, $val);
381
+ }
382
+
383
+ $contactlists_node = $contact_node->addChild("ContactLists");
384
+ foreach ( $lists as $list )
385
+ {
386
+ $listNode = $contactlists_node->addChild("ContactList");
387
+ $listNode->addAttribute("id", $list);
388
+ }
389
+
390
+ $entry = $xml_object->asXML();
391
+
392
+ $url= $this->api['url'].'contacts';
393
+
394
+ $response = Array();
395
+ $response = $this->fetch($url, $entry);
396
+ if ( $this->debug['last_response'] <= 204 )
397
+ return $this->id_from_url($response['entry']['id']['value']);
398
+ else
399
+ return false;
400
+ }
401
+
402
+ /**
403
+ * Returns detailed info for a particular contact.
404
+ *
405
+ * @param Integer $id - Contact ID of the contact requested.
406
+ * @return Associative array containing the contact's details
407
+ */
408
+ public function get_contact($id)
409
+ {
410
+ $url = $this->api['url'].'contacts/'.$id;
411
+ $post = '';
412
+ $contact = Array();
413
+ $contact = $this->format_contact($this->fetch($url,$post));
414
+
415
+ if ($this->debug['last_response'] <= 204)
416
+ return $contact;
417
+ else
418
+ return false;
419
+ }
420
+
421
+ /**
422
+ * Edit a contact
423
+ * See Contact Data Format at http://developer.constantcontact.com/doc/contactCollection for all fields
424
+ *
425
+ * @param Integer $id - Contact ID of the contact to edit
426
+ * @param Array $data - An associative array containing all the contact data to edit
427
+ * @return Boolean - True on success and false on failure
428
+ */
429
+ public function edit_contact($id,$data)
430
+ {
431
+ //Get the old xml from get_contact() and then post after replacing values that need replacing
432
+ $url = $this->api['url'].'contacts/'.$id;
433
+ $post = '';
434
+ $post = $this->fetch($url,$post,'xml');
435
+
436
+ foreach ($data as $key => $val)
437
+ $post = preg_replace("/<$key>.*?<\/{$key}>/s","<$key>$val</$key>",$post);
438
+
439
+ $this->fetch($url,$post,'array','PUT');
440
+
441
+ if ($this->debug['last_response'] <= 204)
442
+ return true;
443
+ else
444
+ return false;
445
+ }
446
+
447
+ /**
448
+ * Delete a contact
449
+ *
450
+ * @param Integer $id - Contact ID of the contact to delete
451
+ * @return Boolean - True on success and false on failure
452
+ */
453
+ public function delete_contact($id)
454
+ {
455
+ //Get the old xml from get_contact() and then post after replacing values that need replacing
456
+ $url = $this->api['url'].'contacts/'.$id;
457
+ $post = '';
458
+ $this->fetch($url,$post,'array','DELETE');
459
+
460
+ if ($this->debug['last_response'] <= 204)
461
+ return true;
462
+ else
463
+ return false;
464
+ }
465
+
466
+
467
+ /**
468
+ * Search for a contact by email
469
+ * See Contact Data Format at http://developer.constantcontact.com/doc/contactCollection for all fields
470
+ *
471
+ * @param String $email - Email address of the contact to search for
472
+ * @param Boolean $id_only - If true will return ContactID found otherwise will return all contact info
473
+ * @return Integer of found ContactID or Associative Array of contact info for found contact
474
+ */
475
+ public function search_contact_by_email($email,$id_only=true)
476
+ {
477
+ //Get the old xml from get_contact() and then post after replacing values that need replacing
478
+ $url = $this->api['url'].'contacts?email='.urlencode(strtolower($email));
479
+ $response = Array();
480
+ $response = $this->fetch($url,$post);
481
+
482
+ if ($this->debug['last_response'] <= 204)
483
+ {
484
+ if ($id_only)
485
+ return $this->id_from_url($response['feed']['entry']['id']['value']);
486
+ else
487
+ return $this->get_contact($this->id_from_url($response['feed']['entry']['id']['value']));
488
+ }
489
+ else
490
+ return false;
491
+ }
492
+
493
+ /**
494
+ * Returns contacts updated since date your list of contacts.
495
+ *
496
+ * @param Date $date - Date should be in the format "Y-m-d H:i:s" (this is the datetime mysql spec)
497
+ * @param String $list_type - Type of contact to query for: active,removed,do-not-mail
498
+ * @param String $key - Optionally specify a key to use for the array
499
+ * @param Boolean $unique - Whether the key you specified is unique
500
+ * @return Associative array containing your contacts indexed by whatever key specified.
501
+ */
502
+ public function search_contacts_by_date($date,$list_type='active',$key='',$unique=true)
503
+ {
504
+ //Format date to UTC atom spec
505
+ $atom_date = date('Y-m-d\TH:i:s\Z',strtotime($date)+date("Z"));
506
+
507
+ $ret = Array();
508
+
509
+ $url = $this->api['url'].'contacts?updatedsince='.$atom_date.'&listtype='.$list_type;
510
+ $post = '';
511
+ $response = Array();
512
+ $response = $this->fetch($url,$post);
513
+
514
+ $entries = Array();
515
+ if (isset($response['feed']['entry']['link']))
516
+ $entries[] = $response['feed']['entry'];
517
+ else
518
+ $entries = $response['feed']['entry'];
519
+ foreach ($entries as $data)
520
+ {
521
+ $my_data = Array();
522
+ $my_data = $data['content']['Contact'];
523
+ $d = Array();
524
+ $d['ContactID'] = $this->id_from_url($my_data['attr']['id']);
525
+ unset($my_data['attr']);
526
+ foreach ($my_data as $dkey => $dval)
527
+ $d[$dkey] = $dval['value'];
528
+
529
+ if ($key)
530
+ {
531
+ if ($unique)
532
+ $ret[$d[$key]] = $d;
533
+ else
534
+ $ret[$d[$key]][] = $d;
535
+ }
536
+ else
537
+ $ret[] = $d;
538
+ }
539
+ return $ret;
540
+ }
541
+
542
+ //List Functions
543
+
544
+ /**
545
+ * Formats a list array into the format we always return as
546
+ * Note:
547
+ * I like to do this because the atom specification has so much extraneous data
548
+ * that I just don't care about it. This makes a sane array out of what is
549
+ * incredibly overcomplicated in my opinion.
550
+ *
551
+ * @param Array - Contact array as returned from fetch/xml_to_array
552
+ * @return Associative array in the format returned by this api
553
+ */
554
+ public function format_list($response)
555
+ {
556
+ $my_data = Array();
557
+ $my_data = $response['entry']['content']['ContactList'];
558
+ $d = Array();
559
+ $d['ListID'] = $this->id_from_url($my_data['attr']['id']);
560
+ unset($my_data['attr']);
561
+ unset($my_data['Members']);
562
+ foreach ($my_data as $key => $val)
563
+ $d[$key] = $val['value'];
564
+ return $d;
565
+ }
566
+
567
+ /**
568
+ * Add a list
569
+ * See List Data Format at http://developer.constantcontact.com/doc/contactLists for all fields
570
+ *
571
+ * @param Array $data - An associative array containing all the contact data to add
572
+ * @return Integer - ListID for the new entry
573
+ */
574
+ public function add_list($data)
575
+ {
576
+ $updated = date('Y-m-d\TH:i:s\Z');
577
+ $post = <<<end
578
+ <entry xmlns="http://www.w3.org/2005/Atom">
579
+ <title type="text"> </title>
580
+ <updated>{$updated}</updated>
581
+ <author></author>
582
+ <id>data:,none</id>
583
+ <summary type="text">Contact</summary>
584
+ <content type="application/vnd.ctct+xml">
585
+ <ContactList xmlns="http://ws.constantcontact.com/ns/1.0/">
586
+
587
+ end;
588
+
589
+ foreach ($data as $key => $val)
590
+ $post .= "\t\t\t<$key>{$val}</$key>\n";
591
+
592
+ $post .= <<<end
593
+ </ContactList>
594
+ </content>
595
+ </entry>
596
+ end;
597
+
598
+
599
+ $url= $this->api['url'].'lists';
600
+
601
+ $response = Array();
602
+ $response = $this->fetch($url,$post);
603
+ if ($this->debug['last_response'] <= 204)
604
+ return $this->id_from_url($response['entry']['id']['value']);
605
+ else
606
+ return false;
607
+ }
608
+
609
+ /**
610
+ * Returns detailed info for a particular list.
611
+ *
612
+ * @param Integer $id - ListID of the list requested.
613
+ * @return Associative array containing the list's details
614
+ */
615
+ public function get_list($id)
616
+ {
617
+ $url = $this->api['url'].'lists/'.$id;
618
+ $post = '';
619
+ $list = Array();
620
+ $list = $this->format_list($this->fetch($url,$post));
621
+
622
+ if ($this->debug['last_response'] <= 204)
623
+ return $list;
624
+ else
625
+ return false;
626
+ }
627
+
628
+ /**
629
+ * Edit a list
630
+ * See List Data Format at http://developer.constantcontact.com/doc/contactLists for all fields
631
+ *
632
+ * @param Integer $id - List ID of the list to edit
633
+ * @param Array $data - An associative array containing all the list data to edit
634
+ * @return Boolean - True on success and false on failure
635
+ */
636
+ public function edit_list($id,$data)
637
+ {
638
+ //Get the old xml from get_list() and then post after replacing values that need replacing
639
+ $url = $this->api['url'].'lists/'.$id;
640
+ $post = '';
641
+ $post = $this->fetch($url,$post,'xml');
642
+
643
+ foreach ($data as $key => $val)
644
+ $post = preg_replace("/<$key>.*?<\/{$key}>/s","<$key>$val</$key>",$post);
645
+
646
+ $this->fetch($url,$post,'array','PUT');
647
+
648
+ if ($this->debug['last_response'] <= 204)
649
+ return true;
650
+ else
651
+ return false;
652
+ }
653
+
654
+ /**
655
+ * Return your lists.
656
+ *
657
+ * @param String $key - Optionally specify a key to use for the array
658
+ * @param Boolean $unique - Whether the key you specified is unique
659
+ * @return Associative array containing your lists indexed by whatever key specified.
660
+ */
661
+ public function get_lists($key='',$unique=true)
662
+ {
663
+ $ret = Array();
664
+
665
+ $url = $this->api['url'].'lists';
666
+ $post = '';
667
+ $response = Array();
668
+ $response = $this->fetch($url,$post);
669
+
670
+ $entries = Array();
671
+ if (isset($response['feed']['entry']['link']))
672
+ $entries[] = $response['feed']['entry'];
673
+ else
674
+ $entries = $response['feed']['entry'];
675
+ foreach ($entries as $data)
676
+ {
677
+ $my_data = Array();
678
+ $my_data = $data['content']['ContactList'];
679
+ $d = Array();
680
+ //$d['ListID'] = $this->id_from_url($my_data['attr']['id']);
681
+ $d['ListID'] = $my_data['attr']['id'];
682
+
683
+ // Exclude the default lists like active, do-not-mail and removed
684
+ if ( ! is_numeric($this->id_from_url($my_data['attr']['id'])) )
685
+ continue;
686
+
687
+ unset($my_data['attr']);
688
+ foreach ($my_data as $dkey => $dval)
689
+ {
690
+ if ( isset($dval['value']) )
691
+ $d[$dkey] = $dval['value'];
692
+ }
693
+
694
+ if ($key)
695
+ {
696
+ if ($unique)
697
+ $ret[$d[$key]] = $d;
698
+ else
699
+ $ret[$d[$key]][] = $d;
700
+ }
701
+ else
702
+ $ret[] = $d;
703
+ }
704
+ return $ret;
705
+ }
706
+
707
+ /**
708
+ * Delete a list
709
+ *
710
+ * @param Integer $id - ListID of the list to delete
711
+ * @return Boolean - True on success and false on failure
712
+ */
713
+ public function delete_list($id)
714
+ {
715
+ //Get the old xml from get_list() and then post after replacing values that need replacing
716
+ $url = $this->api['url'].'lists/'.$id;
717
+ $post = '';
718
+ $this->fetch($url,$post,'array','DELETE');
719
+
720
+ if ($this->debug['last_response'] <= 204)
721
+ return true;
722
+ else
723
+ return false;
724
+ }
725
+
726
+ //Subscription Functions
727
+
728
+ /**
729
+ * Add a subscription for an existing contact to a list
730
+ * See Contact Data Format at http://developer.constantcontact.com/doc/contactCollection for all fields
731
+ *
732
+ * @param Integer $contact_id - Contact ID of the contact to use
733
+ * @param Integer $list_id - List ID of the list to add the contact to
734
+ * @param String $optin_source - ACTION_BY_CUSTOMER or ACTION_BY_CLIENT depending on if this person is filling out a form for subscription in realtime
735
+ * @return Boolean - True on success and false on failure
736
+ */
737
+ public function add_subscription($contact_id,$list_id,$optin_source='ACTION_BY_CUSTOMER')
738
+ {
739
+ //Get the old xml from get_contact() and then post after replacing values that need replacing
740
+ $url = $this->api['url'].'contacts/'.$contact_id;
741
+ $post = '';
742
+ $post = $this->fetch($url,$post,'xml');
743
+
744
+ if (!strstr($post,'</ContactLists>'))
745
+ $post = str_replace('</Contact>',"<ContactLists></ContactLists></Contact>",$post);
746
+
747
+ if (!strstr($post,'<OptInSource>'))
748
+ $post = str_replace('</Confirmed>',"</Confirmed>\n<OptInSource>$optin_source</OptInSource>",$post);
749
+
750
+ if (!strstr($post,'<ContactList id="'.$this->api['inner_url'].'lists/'.$list_id.'">'))
751
+ $post = str_replace('</ContactLists>','<ContactList id="'.$this->api['inner_url'].'lists/'.$list_id.'"></ContactList></ContactLists>',$post);
752
+
753
+ $this->fetch($url,$post,'array','PUT');
754
+
755
+ if ($this->debug['last_response'] <= 204)
756
+ return true;
757
+ else
758
+ return false;
759
+ }
760
+
761
+ /**
762
+ * Remove a subscription for an existing contact to a list
763
+ * See Contact Data Format at http://developer.constantcontact.com/doc/contactCollection for all fields
764
+ *
765
+ * @param Integer $contact_id - Contact ID of the contact to use
766
+ * @param Integer $list_id - List ID of the list to add the contact to
767
+ * @return Boolean - True on success and false on failure
768
+ */
769
+ public function remove_subscription($contact_id,$list_id)
770
+ {
771
+ //Get the old xml from get_contact() and then post after replacing values that need replacing
772
+ $url = $this->api['url'].'contacts/'.$contact_id;
773
+ $post = '';
774
+ $post = $this->fetch($url,$post,'xml');
775
+
776
+ $post = preg_replace('/<ContactList id="'.str_replace('/','\/',$this->api['inner_url']).'lists\/'.$list_id.'">.*?<\/ContactList>/s','',$post);
777
+
778
+ $this->fetch($url,$post,'array','PUT');
779
+
780
+ if ($this->debug['last_response'] <= 204)
781
+ return true;
782
+ else
783
+ return false;
784
+ }
785
+
786
+ //Campaign Functions
787
+
788
+ /**
789
+ * Formats a campaign array into the format we always return as
790
+ * Note:
791
+ * I like to do this because the atom specification has so much extraneous data
792
+ * that I just don't care about it. This makes a sane array out of what is
793
+ * incredibly overcomplicated in my opinion.
794
+ *
795
+ * @param Array - Contact array as returned from fetch/xml_to_array
796
+ * @return Associative array in the format returned by this api
797
+ */
798
+ public function format_campaign($response)
799
+ {
800
+ $my_data = Array();
801
+ $my_data = $response['entry']['content']['Campaign'];
802
+ $d = Array();
803
+ $d['CampaignID'] = $this->id_from_url($my_data['attr']['id']);
804
+ $d['FromID'] = $this->id_from_url($my_data['FromEmail']['Email']['attr']['id']);
805
+ $d['FromEmail'] = $my_data['FromEmail']['EmailAddress']['value'];
806
+ unset($my_data['FromEmail']);
807
+ unset($my_data['ReplyToEmail']);
808
+
809
+ unset($my_data['attr']);
810
+ foreach ($my_data as $dkey => $dval)
811
+ {
812
+ if ($dkey != 'ContactLists')
813
+ $d[$dkey] = $dval['value'];
814
+ else
815
+ {
816
+ $entries = Array();
817
+ if (isset($dval['ContactList']['link']))
818
+ $entries[] = $dval['ContactList'];
819
+ else
820
+ $entries = $dval['ContactList'];
821
+
822
+ foreach ($entries as $entry)
823
+ {
824
+ $e = Array();
825
+ $e['ListID'] = $this->id_from_url($entry['attr']['id']);
826
+ unset($entry['attr']);
827
+ unset($entry['link']);
828
+ foreach ($entry as $ekey => $eval)
829
+ $e[$ekey] = $eval['value'];
830
+ $d[$dkey][] = $e;
831
+ }
832
+ }
833
+ }
834
+ return $d;
835
+ }
836
+
837
+ /**
838
+ * Return your campaigns.
839
+ *
840
+ * @param String $key - Optionally specify a key to use for the array
841
+ * @param Boolean $unique - Whether the key you specified is unique
842
+ * @return Associative array containing your lists indexed by whatever key specified.
843
+ */
844
+ public function get_campaigns($key='',$unique=true)
845
+ {
846
+ $ret = Array();
847
+
848
+ $url = $this->api['url'].'campaigns';
849
+ $post = '';
850
+ $response = Array();
851
+ $response = $this->fetch($url,$post);
852
+
853
+ $entries = Array();
854
+ if (isset($response['feed']['entry']['link']))
855
+ $entries[] = $response['feed']['entry'];
856
+ else
857
+ $entries = $response['feed']['entry'];
858
+ foreach ($entries as $data)
859
+ {
860
+ $my_data = Array();
861
+ $my_data = $data['content']['Campaign'];
862
+ $d = Array();
863
+ $d['CampaignID'] = $this->id_from_url($my_data['attr']['id']);
864
+ unset($my_data['attr']);
865
+ foreach ($my_data as $dkey => $dval)
866
+ $d[$dkey] = $dval['value'];
867
+
868
+ if ($key)
869
+ {
870
+ if ($unique)
871
+ $ret[$d[$key]] = $d;
872
+ else
873
+ $ret[$d[$key]][] = $d;
874
+ }
875
+ else
876
+ $ret[] = $d;
877
+ }
878
+ return $ret;
879
+ }
880
+
881
+ /**
882
+ * Add a campaign
883
+ * See Campaign Data Format at http://developer.constantcontact.com/doc/manageCampaigns for all fields
884
+ *
885
+ * @param Array $data - An associative array containing all the campaign data to add
886
+ * @param Array $lists - An array of ListIDs to have this campaign send to
887
+ * @param Array $from_id - FromID to use as the from email address
888
+ * @return Integer - CampaignID for the new entry
889
+ */
890
+ public function add_campaign($data,$lists,$from_id)
891
+ {
892
+ $updated = date('Y-m-d\TH:i:s\Z');
893
+ $post = <<<end
894
+ <entry xmlns="http://www.w3.org/2005/Atom">
895
+ <link href="{$this->api['relative_url']}campaigns" rel="edit" />
896
+ <id>{$this->api['inner_url']}campaigns</id>
897
+ <title type="text">{$data['Name']}</title>
898
+ <updated>{$updated}</updated>
899
+ <author>
900
+ <name>Constant Contact</name>
901
+ </author>
902
+ <content type="application/vnd.ctct+xml">
903
+ <Campaign xmlns="http://ws.constantcontact.com/ns/1.0/" id="{$this->api['inner_url']}campaigns/">
904
+
905
+ end;
906
+
907
+ foreach ($data as $key => $val)
908
+ {
909
+ if ($key == 'Date')
910
+ $val = date('Y-m-d\TH:i:s\Z',strtotime($val)+date("Z"));
911
+ elseif ($key == 'EmailContent' || $key == 'StyleSheet' || $key == 'PermissionReminderText')
912
+ $val = urlencode($val);
913
+ elseif ($key == 'TextContent')
914
+ $val = urlencode('<Text>'.$val.'</Text>');
915
+
916
+ $post .= "\t\t\t<$key>{$val}</$key>\n";
917
+ }
918
+
919
+ $post .= "\t\t\t<ContactLists>\n";
920
+ foreach ($lists as $list_id)
921
+ $post .= "\t\t\t\t<ContactList id=\"{$this->api['inner_url']}lists/{$list_id}\" />\n";
922
+
923
+ $temp = Array();
924
+ $temp = get_from($from_id);
925
+ $email = $temp['EmailAddress'];
926
+
927
+ $post .= <<<end
928
+ </ContactLists>
929
+ <FromEmail>
930
+ <Email id="{$this->api['inner_url']}emailaddresses/$from_id">
931
+ <link xmlns="http://www.w3.org/2005/Atom" href="{$this->api['inner_url']}emailaddresses/$from_id" rel="self" />
932
+ </Email>
933
+ <EmailAddress>$email</EmailAddress>
934
+ </FromEmail>
935
+ <ReplyToEmail>
936
+ <Email id="{$this->api['inner_url']}emailaddresses/$from_id">
937
+ <link xmlns="http://www.w3.org/2005/Atom" href="{$this->api['inner_url']}emailaddresses/$from_id" rel="self" />
938
+ </Email>
939
+ <EmailAddress>$email</EmailAddress>
940
+ </ReplyToEmail>
941
+
942
+ end;
943
+
944
+ $post .= <<<end
945
+ </Campaign>
946
+ </content>
947
+ </entry>
948
+ end;
949
+
950
+
951
+ $url= $this->api['url'].'contacts';
952
+
953
+ $response = Array();
954
+ $response = $this->fetch($url,$post);
955
+ if ($this->debug['last_response'] <= 204)
956
+ return $this->id_from_url($response['entry']['id']['value']);
957
+ else
958
+ return false;
959
+ }
960
+
961
+ /**
962
+ * Returns detailed info for a particular campaign.
963
+ *
964
+ * @param Integer $id - Campaign ID of the campaign requested.
965
+ * @return Associative array containing the campaign's details
966
+ */
967
+ public function get_campaign($id)
968
+ {
969
+ $url = $this->api['url'].'campaigns/'.$id;
970
+ $post = '';
971
+ $campaign = Array();
972
+ $campaign = $this->format_campaign($this->fetch($url,$post));
973
+
974
+ if ($this->debug['last_response'] <= 204)
975
+ return $campaign;
976
+ else
977
+ return false;
978
+ }
979
+
980
+ /**
981
+ * Edit a campaign
982
+ * See Campaign Data Format at http://developer.constantcontact.com/doc/manageCampaigns for all fields
983
+ *
984
+ * @param Integer $id - Campaign ID of the list to edit
985
+ * @param Array $data - An associative array containing all the campaign data to edit
986
+ * @return Boolean - True on success and false on failure
987
+ */
988
+ public function edit_campaign($id,$data)
989
+ {
990
+ //Get the old xml from get_campaign() and then post after replacing values that need replacing
991
+ $url = $this->api['url'].'campaigns/'.$id;
992
+ $post = '';
993
+ $post = $this->fetch($url,$post,'xml');
994
+
995
+ foreach ($data as $key => $val)
996
+ $post = preg_replace("/<$key>.*?<\/{$key}>/s","<$key>$val</$key>",$post);
997
+
998
+ $this->fetch($url,$post,'array','PUT');
999
+
1000
+ if ($this->debug['last_response'] <= 204)
1001
+ return true;
1002
+ else
1003
+ return false;
1004
+ }
1005
+
1006
+ /**
1007
+ * Delete a campaign
1008
+ *
1009
+ * @param Integer $id - CampaignID of the list to delete
1010
+ * @return Boolean - True on success and false on failure
1011
+ */
1012
+ public function delete_campaign($id)
1013
+ {
1014
+ //Get the old xml from get_campaign() and then post after replacing values that need replacing
1015
+ $url = $this->api['url'].'campaigns/'.$id;
1016
+ $post = '';
1017
+ $this->fetch($url,$post,'array','DELETE');
1018
+
1019
+ if ($this->debug['last_response'] <= 204)
1020
+ return true;
1021
+ else
1022
+ return false;
1023
+ }
1024
+
1025
+ //From Functions
1026
+
1027
+ /**
1028
+ * Formats a list array into the format we always return as
1029
+ * Note:
1030
+ * I like to do this because the atom specification has so much extraneous data
1031
+ * that I just don't care about. This makes a sane array out of what is
1032
+ * incredibly overcomplicated in my opinion.
1033
+ *
1034
+ * @param Array - Contact array as returned from fetch/xml_to_array
1035
+ * @return Associative array in the format returned by this api
1036
+ */
1037
+ public function format_from($response)
1038
+ {
1039
+ $my_data = Array();
1040
+ $my_data = $response['entry']['content']['Email'];
1041
+ $d = Array();
1042
+ $d['FromID'] = $this->id_from_url($my_data['attr']['id']);
1043
+ unset($my_data['attr']);
1044
+ foreach ($my_data as $key => $val)
1045
+ $d[$key] = $val['value'];
1046
+ return $d;
1047
+ }
1048
+
1049
+ /**
1050
+ * Returns detailed info for a particular list.
1051
+ *
1052
+ * @param Integer $id - FromID of the from email requested.
1053
+ * @return Associative array containing the from's details
1054
+ */
1055
+ public function get_from($id)
1056
+ {
1057
+ $url = $this->api['url'].'settings/emailaddresses/'.$id;
1058
+ $post = '';
1059
+ $from = Array();
1060
+ $from = $this->format_from($this->fetch($url,$post));
1061
+
1062
+ if ($this->debug['last_response'] <= 204)
1063
+ return $from;
1064
+ else
1065
+ return false;
1066
+ }
1067
+
1068
+ /**
1069
+ * Return your from email addresses.
1070
+ *
1071
+ * @param String $key - Optionally specify a key to use for the array
1072
+ * @param Boolean $unique - Whether the key you specified is unique
1073
+ * @return Associative array containing your lists indexed by whatever key specified.
1074
+ */
1075
+ public function get_froms($key='',$unique=true)
1076
+ {
1077
+ $ret = Array();
1078
+
1079
+ $url = $this->api['url'].'settings/emailaddresses';
1080
+ $post = '';
1081
+ $response = Array();
1082
+ $response = $this->fetch($url,$post);
1083
+
1084
+ $entries = Array();
1085
+ if (isset($response['feed']['entry']['link']))
1086
+ $entries[] = $response['feed']['entry'];
1087
+ else
1088
+ $entries = $response['feed']['entry'];
1089
+ foreach ($entries as $data)
1090
+ {
1091
+ $my_data = Array();
1092
+ $my_data = $data['content']['Email'];
1093
+ $d = Array();
1094
+ $d['FromID'] = $this->id_from_url($my_data['attr']['id']);
1095
+ unset($my_data['attr']);
1096
+ foreach ($my_data as $dkey => $dval)
1097
+ $d[$dkey] = $dval['value'];
1098
+
1099
+ if ($key)
1100
+ {
1101
+ if ($unique)
1102
+ $ret[$d[$key]] = $d;
1103
+ else
1104
+ $ret[$d[$key]][] = $d;
1105
+ }
1106
+ else
1107
+ $ret[] = $d;
1108
+ }
1109
+ return $ret;
1110
+ }
1111
+
1112
+ }
1113
+
1114
+ ?>
power-ups/contacts/admin/contacts-admin.php CHANGED
@@ -276,13 +276,13 @@ class WPLeadInContactsAdmin extends WPLeadInAdmin {
276
  $q = $wpdb->prepare("SELECT hashkey FROM li_leads WHERE lead_id = %d", $lead_id);
277
  $lead_hash = $wpdb->get_var($q);
278
 
279
- $q = $wpdb->prepare("DELETE FROM li_pageviews WHERE lead_hashkey = %s", $lead_hash);
280
  $delete_pageviews = $wpdb->query($q);
281
 
282
- $q = $wpdb->prepare("DELETE FROM li_submissions WHERE lead_hashkey = %s", $lead_hash);
283
  $delete_submissions = $wpdb->query($q);
284
 
285
- $q = $wpdb->prepare("DELETE FROM li_leads WHERE lead_id = %d", $lead_id);
286
  $delete_lead = $wpdb->query($q);
287
 
288
  return $delete_lead;
276
  $q = $wpdb->prepare("SELECT hashkey FROM li_leads WHERE lead_id = %d", $lead_id);
277
  $lead_hash = $wpdb->get_var($q);
278
 
279
+ $q = $wpdb->prepare("UPDATE li_pageviews SET pageview_deleted = 1 WHERE lead_hashkey = %s AND pageview_deleted = 0", $lead_hash);
280
  $delete_pageviews = $wpdb->query($q);
281
 
282
+ $q = $wpdb->prepare("UPDATE li_submissions SET form_deleted = 1 WHERE lead_hashkey = %s AND form_deleted = 0", $lead_hash);
283
  $delete_submissions = $wpdb->query($q);
284
 
285
+ $q = $wpdb->prepare("UPDATE li_leads SET lead_deleted = 1 WHERE lead_id = %d AND lead_deleted = 0", $lead_id);
286
  $delete_lead = $wpdb->query($q);
287
 
288
  return $delete_lead;
power-ups/mailchimp-list-sync.php CHANGED
@@ -92,7 +92,7 @@ class WPMailChimpListSync extends WPLeadIn {
92
 
93
  if ( isset($options['li_mls_api_key']) && $options['li_mls_api_key'] && isset($options['li_mls_subscribers_to_list']) && $options['li_mls_subscribers_to_list'] )
94
  {
95
- $MailChimp = new MailChimp($options['li_mls_api_key']);
96
 
97
  $subscribe = $MailChimp->call("lists/subscribe", array(
98
  "id" => $options['li_mls_subscribers_to_list'],
92
 
93
  if ( isset($options['li_mls_api_key']) && $options['li_mls_api_key'] && isset($options['li_mls_subscribers_to_list']) && $options['li_mls_subscribers_to_list'] )
94
  {
95
+ $MailChimp = new LI_MailChimp($options['li_mls_api_key']);
96
 
97
  $subscribe = $MailChimp->call("lists/subscribe", array(
98
  "id" => $options['li_mls_subscribers_to_list'],
power-ups/mailchimp-list-sync/admin/mailchimp-list-sync-admin.php CHANGED
@@ -82,25 +82,26 @@ class WPMailChimpListSyncAdmin extends WPLeadInAdmin {
82
  function li_mls_subscribers_to_list_callback ()
83
  {
84
  $options = get_option('leadin_mls_options');
85
- $li_mls_subscribers_to_list = ( isset($options['li_mls_subscribers_to_list']) ? $options['li_mls_subscribers_to_list'] : '' ); // Get header from options, or show default
86
-
87
  $lists = $this->li_mls_get_mailchimp_lists($options['li_mls_api_key']);
88
 
89
  echo '<select id="li_mls_subscribers_to_list" name="leadin_mls_options[li_mls_subscribers_to_list]" ' . ( ! count($lists['data']) ? 'disabled' : '' ) . '>';
90
 
91
  if ( count($lists['data']) )
92
  {
 
 
93
  foreach ( $lists['data'] as $list )
94
  {
95
- $list_set = FALSE;
96
  if ( $list['id'] == $li_mls_subscribers_to_list && !$list_set )
97
  $list_set = TRUE;
98
 
99
  echo '<option ' . ( $list['id'] == $li_mls_subscribers_to_list ? 'selected' : '' ) . ' value="' . $list['id'] . '">' . $list['name'] . '</option>';
100
-
101
- if ( !$list_set )
102
- echo '<option selected value="">No list set...</option>';
103
  }
 
 
 
104
  }
105
  else
106
  {
@@ -114,7 +115,7 @@ class WPMailChimpListSyncAdmin extends WPLeadInAdmin {
114
 
115
  function li_mls_get_mailchimp_lists ( $api_key )
116
  {
117
- $MailChimp = new MailChimp($api_key);
118
 
119
  $lists = $MailChimp->call("lists/list", array(
120
  "start" => 0, // optional, control paging of lists, start results at this list #, defaults to 1st page of data (page 0)
82
  function li_mls_subscribers_to_list_callback ()
83
  {
84
  $options = get_option('leadin_mls_options');
85
+ $li_mls_subscribers_to_list = ( isset($options['li_mls_subscribers_to_list']) ? $options['li_mls_subscribers_to_list'] : '' );
86
+
87
  $lists = $this->li_mls_get_mailchimp_lists($options['li_mls_api_key']);
88
 
89
  echo '<select id="li_mls_subscribers_to_list" name="leadin_mls_options[li_mls_subscribers_to_list]" ' . ( ! count($lists['data']) ? 'disabled' : '' ) . '>';
90
 
91
  if ( count($lists['data']) )
92
  {
93
+ $list_set = FALSE;
94
+
95
  foreach ( $lists['data'] as $list )
96
  {
 
97
  if ( $list['id'] == $li_mls_subscribers_to_list && !$list_set )
98
  $list_set = TRUE;
99
 
100
  echo '<option ' . ( $list['id'] == $li_mls_subscribers_to_list ? 'selected' : '' ) . ' value="' . $list['id'] . '">' . $list['name'] . '</option>';
 
 
 
101
  }
102
+
103
+ if ( !$list_set )
104
+ echo '<option selected value="">No list set...</option>';
105
  }
106
  else
107
  {
115
 
116
  function li_mls_get_mailchimp_lists ( $api_key )
117
  {
118
+ $MailChimp = new LI_MailChimp($api_key);
119
 
120
  $lists = $MailChimp->call("lists/list", array(
121
  "start" => 0, // optional, control paging of lists, start results at this list #, defaults to 1st page of data (page 0)
power-ups/mailchimp-list-sync/inc/MailChimp-API.php CHANGED
@@ -14,7 +14,7 @@
14
  * @author Drew McLellan <drew.mclellan@gmail.com>
15
  * @version 1.1.1
16
  */
17
- class MailChimp
18
  {
19
  private $api_key;
20
  private $api_endpoint = 'https://<dc>.api.mailchimp.com/2.0';
14
  * @author Drew McLellan <drew.mclellan@gmail.com>
15
  * @version 1.1.1
16
  */
17
+ class LI_MailChimp
18
  {
19
  private $api_key;
20
  private $api_endpoint = 'https://<dc>.api.mailchimp.com/2.0';
power-ups/subscribe-widget.php CHANGED
@@ -80,7 +80,9 @@ class WPLeadInSubscribe extends WPLeadIn {
80
  'li_susbscibe_installed' => '1',
81
  'li_subscribe_vex_class' => 'vex-theme-bottom-right-corner',
82
  'li_subscribe_heading' => 'Sign up for my newsletter to get new posts by email',
83
- 'li_subscribe_btn_label' => 'SUBSCRIBE'
 
 
84
 
85
  );
86
 
@@ -101,6 +103,8 @@ class WPLeadInSubscribe extends WPLeadIn {
101
  echo '<input id="leadin-subscribe-vex-class" value="' . ( isset($lis_options['li_subscribe_vex_class']) ? $lis_options['li_subscribe_vex_class'] : 'vex-theme-bottom-right-corner' ) . '" type="hidden"/>';
102
  echo '<input id="leadin-subscribe-heading" value="' . ( isset($lis_options['li_subscribe_heading']) ? $lis_options['li_subscribe_heading'] : 'Sign up for my newsletter to get new posts by email' ) . '" type="hidden"/>';
103
  echo '<input id="leadin-subscribe-btn-label" value="' . ( isset($lis_options['li_subscribe_btn_label']) ? $lis_options['li_subscribe_btn_label'] : 'SUBSCRIBE' ) . '" type="hidden"/>';
 
 
104
 
105
  // Div checked by media query for mobile
106
  echo '<span id="leadin-subscribe-mobile-check"></span>';
@@ -128,9 +132,11 @@ class WPLeadInSubscribe extends WPLeadIn {
128
  wp_enqueue_script('vex-dialog');
129
 
130
  wp_register_style('leadin-subscribe-vex-css', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/css/vex.css');
131
-
132
  wp_enqueue_style('leadin-subscribe-vex-css');
133
 
 
 
 
134
  //wp_localize_script('leadin', 'li_ajax', array('ajax_url' => admin_url('admin-ajax.php')));
135
  }
136
  }
80
  'li_susbscibe_installed' => '1',
81
  'li_subscribe_vex_class' => 'vex-theme-bottom-right-corner',
82
  'li_subscribe_heading' => 'Sign up for my newsletter to get new posts by email',
83
+ 'li_subscribe_btn_label' => 'SUBSCRIBE',
84
+ 'li_subscribe_name_fields' => '0',
85
+ 'li_subscribe_phone_field' => '0'
86
 
87
  );
88
 
103
  echo '<input id="leadin-subscribe-vex-class" value="' . ( isset($lis_options['li_subscribe_vex_class']) ? $lis_options['li_subscribe_vex_class'] : 'vex-theme-bottom-right-corner' ) . '" type="hidden"/>';
104
  echo '<input id="leadin-subscribe-heading" value="' . ( isset($lis_options['li_subscribe_heading']) ? $lis_options['li_subscribe_heading'] : 'Sign up for my newsletter to get new posts by email' ) . '" type="hidden"/>';
105
  echo '<input id="leadin-subscribe-btn-label" value="' . ( isset($lis_options['li_subscribe_btn_label']) ? $lis_options['li_subscribe_btn_label'] : 'SUBSCRIBE' ) . '" type="hidden"/>';
106
+ echo '<input id="leadin-subscribe-name-fields" value="' . ( isset($lis_options['li_subscribe_name_fields']) ? $lis_options['li_subscribe_name_fields'] : '0' ) . '" type="hidden"/>';
107
+ echo '<input id="leadin-subscribe-phone-field" value="' . ( isset($lis_options['li_subscribe_phone_field']) ? $lis_options['li_subscribe_phone_field'] : '0' ) . '" type="hidden"/>';
108
 
109
  // Div checked by media query for mobile
110
  echo '<span id="leadin-subscribe-mobile-check"></span>';
132
  wp_enqueue_script('vex-dialog');
133
 
134
  wp_register_style('leadin-subscribe-vex-css', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/css/vex.css');
 
135
  wp_enqueue_style('leadin-subscribe-vex-css');
136
 
137
+ wp_register_style('leadin-subscribe', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/css/leadin-subscribe.css');
138
+ wp_enqueue_style('leadin-subscribe');
139
+
140
  //wp_localize_script('leadin', 'li_ajax', array('ajax_url' => admin_url('admin-ajax.php')));
141
  }
142
  }
power-ups/subscribe-widget/admin/subscribe-widget-admin.php CHANGED
@@ -40,10 +40,48 @@ class WPLeadInSubscribeAdmin extends WPLeadInAdmin {
40
  {
41
  register_setting('leadin_settings_options', 'leadin_subscribe_options', array($this, 'sanitize'));
42
 
43
- add_settings_section($this->power_up_settings_section, $this->power_up_icon . 'Subscribe Pop-up', '', LEADIN_ADMIN_PATH);
44
- add_settings_field('li_subscribe_vex_class', 'Pop-up Location', array($this, 'li_subscribe_vex_class_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
45
- add_settings_field('li_subscribe_heading', 'Pop-up header text', array($this, 'li_subscribe_heading_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
46
- add_settings_field('li_subscribe_btn_label', 'Button text', array($this, 'li_subscribe_btn_label_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  /**
@@ -64,6 +102,12 @@ class WPLeadInSubscribeAdmin extends WPLeadInAdmin {
64
  if( isset( $input['li_subscribe_btn_label'] ) )
65
  $new_input['li_subscribe_btn_label'] = sanitize_text_field( $input['li_subscribe_btn_label'] );
66
 
 
 
 
 
 
 
67
  return $new_input;
68
  }
69
 
@@ -77,7 +121,7 @@ class WPLeadInSubscribeAdmin extends WPLeadInAdmin {
77
 
78
  echo '<select id="li_subscribe_vex_class" name="leadin_subscribe_options[li_subscribe_vex_class]">';
79
  echo '<option value="vex-theme-bottom-right-corner"' . ( $li_subscribe_vex_class == 'vex-theme-bottom-right-corner' ? ' selected' : '' ) . '>Bottom right</option>';
80
- echo '<option value="vex-theme-bottom-left-corner"' . ( $li_subscribe_vex_class == 'vex-theme-bottom-left-corner' ? ' selected' : '' ) . '>Bottom Left</option>';
81
  echo '<option value="vex-theme-top"' . ( $li_subscribe_vex_class == 'vex-theme-top' ? ' selected' : '' ) . '>Top</option>';
82
  echo '<option value="vex-theme-default"' . ( $li_subscribe_vex_class == 'vex-theme-default' ? ' selected' : '' ) . '>Pop-over content</option>';
83
  echo '</select>';
@@ -92,7 +136,7 @@ class WPLeadInSubscribeAdmin extends WPLeadInAdmin {
92
  $li_subscribe_heading = ( $options['li_subscribe_heading'] ? $options['li_subscribe_heading'] : 'Sign up for my newsletter to get new posts by email' ); // Get header from options, or show default
93
 
94
  printf(
95
- '<input id="li_subscribe_heading" type="text" id="title" name="leadin_subscribe_options[li_subscribe_heading]" value="%s" size="50"/>',
96
  $li_subscribe_heading
97
  );
98
  }
@@ -106,11 +150,40 @@ class WPLeadInSubscribeAdmin extends WPLeadInAdmin {
106
  $li_subscribe_btn_label = ( $options['li_subscribe_btn_label'] ? $options['li_subscribe_btn_label'] : 'SUBSCRIBE' ); // Get button text from options, or show default
107
 
108
  printf(
109
- '<input id="li_subscribe_btn_label" type="text" id="title" name="leadin_subscribe_options[li_subscribe_btn_label]" value="%s" size="50"/>',
110
  $li_subscribe_btn_label
111
  );
112
 
113
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  }
115
 
116
- ?>
40
  {
41
  register_setting('leadin_settings_options', 'leadin_subscribe_options', array($this, 'sanitize'));
42
 
43
+ add_settings_section(
44
+ $this->power_up_settings_section,
45
+ $this->power_up_icon . 'Subscribe Pop-up',
46
+ '',
47
+ LEADIN_ADMIN_PATH
48
+ );
49
+
50
+ add_settings_field(
51
+ 'li_subscribe_vex_class',
52
+ 'Pop-up Location',
53
+ array($this, 'li_subscribe_vex_class_callback'),
54
+ LEADIN_ADMIN_PATH,
55
+ $this->power_up_settings_section
56
+ );
57
+ add_settings_field(
58
+ 'li_subscribe_heading',
59
+ 'Pop-up header text',
60
+ array($this, 'li_subscribe_heading_callback'),
61
+ LEADIN_ADMIN_PATH,
62
+ $this->power_up_settings_section
63
+ );
64
+ add_settings_field(
65
+ 'li_subscribe_btn_label',
66
+ 'Button text',
67
+ array($this, 'li_subscribe_btn_label_callback'),
68
+ LEADIN_ADMIN_PATH,
69
+ $this->power_up_settings_section
70
+ );
71
+ add_settings_field(
72
+ 'li_subscribe_name_fields',
73
+ 'Include First and Last Name',
74
+ array($this, 'li_subscribe_name_fields_callback'),
75
+ LEADIN_ADMIN_PATH,
76
+ $this->power_up_settings_section
77
+ );
78
+ add_settings_field(
79
+ 'li_subscribe_phone_field',
80
+ 'Include Phone Number',
81
+ array($this, 'li_subscribe_phone_field_callback'),
82
+ LEADIN_ADMIN_PATH,
83
+ $this->power_up_settings_section
84
+ );
85
  }
86
 
87
  /**
102
  if( isset( $input['li_subscribe_btn_label'] ) )
103
  $new_input['li_subscribe_btn_label'] = sanitize_text_field( $input['li_subscribe_btn_label'] );
104
 
105
+ if( isset( $input['li_subscribe_name_fields'] ) )
106
+ $new_input['li_subscribe_name_fields'] = sanitize_text_field( $input['li_subscribe_name_fields'] );
107
+
108
+ if( isset( $input['li_subscribe_phone_field'] ) )
109
+ $new_input['li_subscribe_phone_field'] = sanitize_text_field( $input['li_subscribe_phone_field'] );
110
+
111
  return $new_input;
112
  }
113
 
121
 
122
  echo '<select id="li_subscribe_vex_class" name="leadin_subscribe_options[li_subscribe_vex_class]">';
123
  echo '<option value="vex-theme-bottom-right-corner"' . ( $li_subscribe_vex_class == 'vex-theme-bottom-right-corner' ? ' selected' : '' ) . '>Bottom right</option>';
124
+ echo '<option value="vex-theme-bottom-left-corner"' . ( $li_subscribe_vex_class == 'vex-theme-bottom-left-corner' ? ' selected' : '' ) . '>Bottom left</option>';
125
  echo '<option value="vex-theme-top"' . ( $li_subscribe_vex_class == 'vex-theme-top' ? ' selected' : '' ) . '>Top</option>';
126
  echo '<option value="vex-theme-default"' . ( $li_subscribe_vex_class == 'vex-theme-default' ? ' selected' : '' ) . '>Pop-over content</option>';
127
  echo '</select>';
136
  $li_subscribe_heading = ( $options['li_subscribe_heading'] ? $options['li_subscribe_heading'] : 'Sign up for my newsletter to get new posts by email' ); // Get header from options, or show default
137
 
138
  printf(
139
+ '<input id="li_subscribe_heading" type="text" name="leadin_subscribe_options[li_subscribe_heading]" value="%s" size="50"/>',
140
  $li_subscribe_heading
141
  );
142
  }
150
  $li_subscribe_btn_label = ( $options['li_subscribe_btn_label'] ? $options['li_subscribe_btn_label'] : 'SUBSCRIBE' ); // Get button text from options, or show default
151
 
152
  printf(
153
+ '<input id="li_subscribe_btn_label" type="text" name="leadin_subscribe_options[li_subscribe_btn_label]" value="%s" size="50"/>',
154
  $li_subscribe_btn_label
155
  );
156
 
157
  }
158
+
159
+ /**
160
+ * Prints first and last name checkbox for settings page
161
+ */
162
+ function li_subscribe_name_fields_callback ()
163
+ {
164
+ $options = get_option('leadin_subscribe_options');
165
+ $li_subscribe_name_fields = ( $options['li_subscribe_name_fields'] ? $options['li_subscribe_name_fields'] : '0' ); // Get name field options from options, or show default
166
+
167
+ printf(
168
+ '<input id="li_subscribe_name_fields" type="checkbox" name="leadin_subscribe_options[li_subscribe_name_fields]" value="1"' . checked( 1, $options['li_subscribe_name_fields'], false ) . '/>',
169
+ $li_subscribe_name_fields
170
+ );
171
+ }
172
+
173
+ /**
174
+ * Prints phone number checkbox for settings page
175
+ */
176
+ function li_subscribe_phone_field_callback ()
177
+ {
178
+ $options = get_option('leadin_subscribe_options');
179
+ $li_subscribe_phone_field = ( $options['li_subscribe_phone_field'] ? $options['li_subscribe_phone_field'] : '0' ); // Get phone field preference from options, or show default
180
+
181
+ printf(
182
+ '<input id="li_subscribe_phone_field" type="checkbox" name="leadin_subscribe_options[li_subscribe_phone_field]" value="1"' . checked( 1, $options['li_subscribe_phone_field'], false ) . '/>',
183
+ $li_subscribe_phone_field
184
+ );
185
+ }
186
+
187
  }
188
 
189
+ ?>
power-ups/subscribe-widget/frontend/css/leadin-subscribe.css DELETED
@@ -1,47 +0,0 @@
1
- .leadin-subscribe .vex-dialog-message{
2
- font-weight: bold;
3
- margin-bottom: 15px !important;
4
- font-size: 18px !important;
5
- }
6
-
7
- .leadin-subscribe .vex-dialog-button{
8
- background: #444444 !important;
9
- margin-top: -10px !important;
10
- }
11
-
12
- .leadin-subscribe .vex-dialog-input input{
13
- margin-bottom: 10px !important;
14
- font-size: 18px !important;
15
- }
16
-
17
- .leadin-subscribe .vex-dialog-buttons input{
18
- font-size: 14px !important;
19
- color: #fff !important;
20
- }
21
-
22
- .leadin-subscribe .vex-close{
23
- top: 5px !important;
24
- right: 5px !important;
25
- }
26
-
27
- #leadin-subscribe-powered-by{
28
- clear: both;
29
- float: right;
30
- font-size: 12px;
31
- padding-top: 15px;
32
- text-decoration: none;
33
- font-weight: bold;
34
- color: #3288e6;
35
- }
36
-
37
- #leadin-subscribe-powered-by:hover{
38
- text-decoration: underline;
39
- }
40
-
41
- .leadin-subscribe .vex-content{
42
- padding: 15px 15px 10px 15px !important;
43
- }
44
-
45
- @media only screen and (max-width: 760px) {
46
- #leadin-subscribe-mobile-check { display: none; }
47
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
power-ups/subscribe-widget/frontend/js/leadin-subscribe.js CHANGED
@@ -38,7 +38,7 @@ function bind_leadin_subscribe_widget ()
38
  if ($(window).scrollTop() + $(window).height() > $(document).height() / 2) {
39
  subscribe.open();
40
  } else {
41
- subscribe.close();
42
  }
43
  });
44
  };
@@ -52,15 +52,16 @@ function bind_leadin_subscribe_widget ()
52
  showCloseButton: true,
53
  className: 'leadin-subscribe ' + $('#leadin-subscribe-vex-class').val(),
54
  message: $('#leadin-subscribe-heading').val(),
55
- input: '' +
56
- '<input id="leadin-subscribe-email" name="email" type="email" placeholder="Email address" required />',
 
57
  buttons: [$.extend({}, vex.dialog.buttons.YES, { text: ( $('#leadin-subscribe-btn-label').val() ? $('#leadin-subscribe-btn-label').val() : 'SUBSCRIBE' ) })],
58
  onSubmit: function ( data )
59
  {
60
  $('.vex-dialog-form').fadeOut(300, function ( e ) {
61
  $('.vex-dialog-form').html(
62
  '<div class="vex-close"></div>' +
63
- '<h3>Thanks for Subscribing</h3>You should receive a confirmation email in your inbox shortly.' +
64
  '<div>' +
65
  '<span class="powered-by">Powered by LeadIn</span>' +
66
  '<a href="http://leadin.com/wordpress-subscribe-widget/?utm_campaign=subscribe_widget&utm_medium=email&utm_source=' + window.location.host + '"><img alt="LeadIn" height="20px" width="99px" src="http://leadin.com/wp-content/themes/LeadIn-WP-Theme/library/images/logos/Leadin_logo@2x.png" alt="leadin.com"/></a>' +
@@ -81,7 +82,7 @@ function bind_leadin_subscribe_widget ()
81
  }
82
  });
83
 
84
- leadin_subscribe_show();
85
 
86
  $('.leadin-subscribe form.vex-dialog-form').append('<a href="http://leadin.com/pop-subscribe-form-plugin-wordpress/?utm_campaign=subscribe_widget&utm_medium=widget&utm_source=' + document.URL + '" id="leadin-subscribe-powered-by" class="leadin-subscribe-powered-by">Powered by LeadIn</a>');
87
  };
38
  if ($(window).scrollTop() + $(window).height() > $(document).height() / 2) {
39
  subscribe.open();
40
  } else {
41
+ //subscribe.close();
42
  }
43
  });
44
  };
52
  showCloseButton: true,
53
  className: 'leadin-subscribe ' + $('#leadin-subscribe-vex-class').val(),
54
  message: $('#leadin-subscribe-heading').val(),
55
+ input: '<input id="leadin-subscribe-email" name="email" type="email" placeholder="Email address" required />' +
56
+ (($('#leadin-subscribe-name-fields').val()==0) ? '' : '<input id="leadin-subscribe-fname" name="fname" type="text" placeholder="First Name" required /><input id="leadin-subscribe-lname" name="lname" type="text" placeholder="Last Name" required />') +
57
+ (($('#leadin-subscribe-phone-field').val()==0) ? '' : '<input id="leadin-subscribe-phone" name="phone" type="tel" placeholder="Phone" required />'),
58
  buttons: [$.extend({}, vex.dialog.buttons.YES, { text: ( $('#leadin-subscribe-btn-label').val() ? $('#leadin-subscribe-btn-label').val() : 'SUBSCRIBE' ) })],
59
  onSubmit: function ( data )
60
  {
61
  $('.vex-dialog-form').fadeOut(300, function ( e ) {
62
  $('.vex-dialog-form').html(
63
  '<div class="vex-close"></div>' +
64
+ '<h3>Thanks!<br>You should receive a confirmation email in your inbox shortly.</h3>' +
65
  '<div>' +
66
  '<span class="powered-by">Powered by LeadIn</span>' +
67
  '<a href="http://leadin.com/wordpress-subscribe-widget/?utm_campaign=subscribe_widget&utm_medium=email&utm_source=' + window.location.host + '"><img alt="LeadIn" height="20px" width="99px" src="http://leadin.com/wp-content/themes/LeadIn-WP-Theme/library/images/logos/Leadin_logo@2x.png" alt="leadin.com"/></a>' +
82
  }
83
  });
84
 
85
+ //leadin_subscribe_show();
86
 
87
  $('.leadin-subscribe form.vex-dialog-form').append('<a href="http://leadin.com/pop-subscribe-form-plugin-wordpress/?utm_campaign=subscribe_widget&utm_medium=widget&utm_source=' + document.URL + '" id="leadin-subscribe-powered-by" class="leadin-subscribe-powered-by">Powered by LeadIn</a>');
88
  };
readme.txt CHANGED
@@ -2,8 +2,8 @@
2
  Contributors: andygcook, nelsonjoyce
3
  Tags: lead tracking, visitor tracking, analytics, crm, marketing automation, inbound marketing, subscription, marketing, lead generation, mailchimp
4
  Requires at least: 3.7
5
- Tested up to: 3.8.2
6
- Stable tag: 0.7.0
7
 
8
  LeadIn is an easy-to-use marketing automation and lead tracking plugin for WordPress that helps you better understand your web site visitors.
9
 
@@ -85,17 +85,59 @@ To ensure quality we've tested the most popular WordPress form builder plugins.
85
  1. Individual contact history
86
  2. Contacts list
87
  3. Sample email report
 
88
 
89
  == Changelog ==
90
 
91
- Current version: 0.7.0
92
- Current version release: 2014-04-10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
  = 0.7.0 (2014.04.10) =
95
 
96
  = Enhancements =
97
  - MailChimp List Sync power-up
98
- - Added new themes (bottom right, bottom left, top and pop-up) to the WordPerss Subscribe Widget power-up
99
 
100
  = 0.6.2 (2014.04.07) =
101
  - Bug fixes
2
  Contributors: andygcook, nelsonjoyce
3
  Tags: lead tracking, visitor tracking, analytics, crm, marketing automation, inbound marketing, subscription, marketing, lead generation, mailchimp
4
  Requires at least: 3.7
5
+ Tested up to: 3.9.0
6
+ Stable tag: 0.8.3
7
 
8
  LeadIn is an easy-to-use marketing automation and lead tracking plugin for WordPress that helps you better understand your web site visitors.
9
 
85
  1. Individual contact history
86
  2. Contacts list
87
  3. Sample email report
88
+ 4. Pop-up subscribe widget
89
 
90
  == Changelog ==
91
 
92
+ - Current version: 0.8.3
93
+ - Current version release: 2014-05-06
94
+
95
+ = 0.8.3 (2014.05.06) =
96
+ - Bug fixes
97
+ - Merge duplicate contacts into one record
98
+ - Remove url parameters from source links in contact list
99
+ - Downgrade use of singletons so classes are compatabile with PHP 5.2
100
+
101
+ = Enhancements =
102
+ - Swap out delete statements in favor of binary "deleted" flags to minimize data loss risk
103
+ - Sort contacts by last visit
104
+
105
+ = 0.8.2 (2014.05.02) =
106
+ - Bug fixes
107
+ - Removed namespace usage in favor or a low-tech work around to be compliant with PHP 5.2 and lower
108
+
109
+ = 0.8.1 (2014.04.30) =
110
+ - Bug fixes
111
+ - Namespaced duplicate classes
112
+
113
+ = 0.8.0 (2014.04.30) =
114
+ - Bug fixes
115
+ - Fix scrolling issue with subscribe pop-up
116
+ - Duplicate class bug fixes
117
+
118
+ = Enhancements =
119
+ - Add optional first name, last name and phone fields for subscribe pop-up
120
+ - Change out contact notification emails to be from settings email address
121
+ - Ability to disable contact notification emails
122
+ - Constant Contact list sync power-up
123
+ - Sync optional contact fields (name + phone) to email service provider power-ups
124
+
125
+ = 0.7.2 (2014.04.18) =
126
+ - Bug fixes
127
+ - Fix contact deletion bug
128
+ - Implement data recovery fix for contacts
129
+ - Bug fixes to contact merging
130
+
131
+
132
+ = 0.7.1 (2014.04.11) =
133
+ - Bug fixes
134
+ - SVN bug fix that did not add the MailChimp List sync power-up
135
 
136
  = 0.7.0 (2014.04.10) =
137
 
138
  = Enhancements =
139
  - MailChimp List Sync power-up
140
+ - Added new themes (bottom right, bottom left, top and pop-up) to the WordPress Subscribe Widget power-up
141
 
142
  = 0.6.2 (2014.04.07) =
143
  - Bug fixes
screenshot-4.png ADDED
Binary file