HubSpot – Free Marketing Plugin for WordPress - Version 0.6.0

Version Description

(2014.03.07) = - Bug fixes - Remove in-house plugin updating functionality - Original referrer is always the server url, not the HTTP referrer - Strip slashes from title tags - Number of contacts does not equal leads + commenters + subscribers - Modals aren't bound to forms after page load - Fix bug with activating + reactivating the plugin overwriting the saved settings - Override button styles for Subscription Pop-in widget

Download this release

Release Info

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

Version 0.6.0

Files changed (90) hide show
  1. admin/css/leadin-admin.css +512 -0
  2. admin/inc/class-leadin-contact.php +200 -0
  3. admin/inc/class-leadin-list-table.php +455 -0
  4. admin/inc/class-leadin-pointers.php +117 -0
  5. admin/js/leadin-admin.js +3 -0
  6. admin/leadin-admin.php +503 -0
  7. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_appearance.scssc +0 -0
  8. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-clip.scssc +0 -0
  9. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-origin.scssc +0 -0
  10. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-size.scssc +0 -0
  11. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_border-radius.scssc +0 -0
  12. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-shadow.scssc +0 -0
  13. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-sizing.scssc +0 -0
  14. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box.scssc +0 -0
  15. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_columns.scssc +0 -0
  16. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_filter.scssc +0 -0
  17. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_font-face.scssc +0 -0
  18. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_hyphenation.scssc +0 -0
  19. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_images.scssc +0 -0
  20. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_inline-block.scssc +0 -0
  21. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_opacity.scssc +0 -0
  22. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_regions.scssc +0 -0
  23. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_shared.scssc +0 -0
  24. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_text-shadow.scssc +0 -0
  25. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transform.scssc +0 -0
  26. admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transition.scssc +0 -0
  27. admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_clearfix.scssc +0 -0
  28. admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_hacks.scssc +0 -0
  29. admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_css3.scssc +0 -0
  30. admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_support.scssc +0 -0
  31. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contact_detail_page.sassc +0 -0
  32. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contacts_list_page.sassc +0 -0
  33. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_settings_page.sassc +0 -0
  34. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_tables.sassc +0 -0
  35. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_variables.sassc +0 -0
  36. admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/leadin-admin.sassc +0 -0
  37. admin/sass/_contact_detail_page.sass +116 -0
  38. admin/sass/_contacts_list_page.sass +13 -0
  39. admin/sass/_grid.sass +22 -0
  40. admin/sass/_powerups_page.sass +27 -0
  41. admin/sass/_settings_page.sass +95 -0
  42. admin/sass/_tables.sass +81 -0
  43. admin/sass/_variables.sass +60 -0
  44. admin/sass/config.rb +30 -0
  45. admin/sass/leadin-admin.sass +55 -0
  46. frontend/js/jquery.cookie.js +113 -0
  47. frontend/js/leadin.js +435 -0
  48. images/checkmark.png +0 -0
  49. images/leadin-icon-32x32.png +0 -0
  50. images/leadin-svg-icon.svg +11 -0
  51. images/powerup-icon-analytics.png +0 -0
  52. images/powerup-icon-analytics@2x.png +0 -0
  53. images/powerup-icon-ideas.png +0 -0
  54. images/powerup-icon-ideas@2x.png +0 -0
  55. images/powerup-icon-leads.png +0 -0
  56. images/powerup-icon-leads@2x.png +0 -0
  57. images/powerup-icon-subscribe.png +0 -0
  58. images/powerup-icon-subscribe@2x.png +0 -0
  59. images/powerups.png +0 -0
  60. images/triangle.png +0 -0
  61. inc/class-emailer.php +428 -0
  62. inc/class-leadin-options.php +149 -0
  63. inc/leadin-ajax-functions.php +312 -0
  64. inc/leadin-functions.php +250 -0
  65. leadin.php +477 -0
  66. lib/mixpanel/Base/MixpanelBase.php +65 -0
  67. lib/mixpanel/ConsumerStrategies/AbstractConsumer.php +57 -0
  68. lib/mixpanel/ConsumerStrategies/CurlConsumer.php +221 -0
  69. lib/mixpanel/ConsumerStrategies/FileConsumer.php +38 -0
  70. lib/mixpanel/ConsumerStrategies/SocketConsumer.php +308 -0
  71. lib/mixpanel/Mixpanel.php +302 -0
  72. lib/mixpanel/Producers/MixpanelBaseProducer.php +229 -0
  73. lib/mixpanel/Producers/MixpanelEvents.php +164 -0
  74. lib/mixpanel/Producers/MixpanelPeople.php +138 -0
  75. power-ups/contacts.php +95 -0
  76. power-ups/contacts/admin/contacts-admin.php +385 -0
  77. power-ups/contacts/admin/css/leadin-contacts-admin.css +12 -0
  78. power-ups/contacts/admin/js/leadin-contacts-admin.js +72 -0
  79. power-ups/contacts/images/powerup-icon-contacts@2x.png +0 -0
  80. power-ups/subscribe-widget.php +182 -0
  81. power-ups/subscribe-widget/admin/subscribe-widget-admin.php +109 -0
  82. power-ups/subscribe-widget/frontend/css/leadin-subscribe.css +47 -0
  83. power-ups/subscribe-widget/frontend/css/vex.css +976 -0
  84. power-ups/subscribe-widget/frontend/js/leadin-subscribe.js +99 -0
  85. power-ups/subscribe-widget/frontend/js/vex.dialog.js +149 -0
  86. power-ups/subscribe-widget/frontend/js/vex.js +189 -0
  87. readme.txt +225 -0
  88. screenshot-1.png +0 -0
  89. screenshot-2.png +0 -0
  90. screenshot-3.png +0 -0
admin/css/leadin-admin.css ADDED
@@ -0,0 +1,512 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* line 12, ../sass/_grid.sass */
2
+ #leadin {
3
+ *zoom: 1;
4
+ max-width: 1180px;
5
+ _width: 1180px;
6
+ padding-left: 20px;
7
+ padding-right: 20px;
8
+ margin-left: auto;
9
+ margin-right: auto;
10
+ margin-left: 0;
11
+ padding: 0;
12
+ }
13
+ /* line 38, ../../../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
14
+ #leadin:after {
15
+ content: "";
16
+ display: table;
17
+ clear: both;
18
+ }
19
+ @media (min-width: 782px) {
20
+ /* line 12, ../sass/_grid.sass */
21
+ #leadin {
22
+ max-width: 1180px;
23
+ }
24
+ }
25
+ /* line 17, ../sass/_grid.sass */
26
+ #leadin * {
27
+ box-sizing: border-box;
28
+ }
29
+
30
+ /* line 6, ../sass/_tables.sass */
31
+ #leadin .top_table_controls {
32
+ border-bottom: 2px solid #dedede;
33
+ margin: 0 0 18px 0;
34
+ margin-bottom: 16px;
35
+ *zoom: 1;
36
+ }
37
+ /* line 38, ../../../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
38
+ #leadin .top_table_controls:after {
39
+ content: "";
40
+ display: table;
41
+ clear: both;
42
+ }
43
+ /* line 12, ../sass/_tables.sass */
44
+ #leadin .table_segment_picker {
45
+ float: left;
46
+ margin: 0;
47
+ }
48
+ /* line 16, ../sass/_tables.sass */
49
+ #leadin .table_segment_picker li {
50
+ display: inline-block;
51
+ margin: 0;
52
+ }
53
+ /* line 20, ../sass/_tables.sass */
54
+ #leadin .table_segment_picker li + li {
55
+ margin-left: 2em;
56
+ }
57
+ /* line 22, ../sass/_tables.sass */
58
+ #leadin .table_segment_picker li a {
59
+ display: block;
60
+ padding: 12px 0;
61
+ line-height: 24px;
62
+ font-weight: 300;
63
+ font-size: 20px;
64
+ text-decoration: none;
65
+ }
66
+ /* line 30, ../sass/_tables.sass */
67
+ #leadin .table_segment_picker li a.current {
68
+ margin-bottom: -2px;
69
+ font-weight: 400;
70
+ border-bottom: 2px solid #f66000;
71
+ }
72
+ /* line 35, ../sass/_tables.sass */
73
+ #leadin .table_segment_picker li a.current, #leadin .table_segment_picker li a:hover, #leadin .table_segment_picker li a:active {
74
+ color: #f66000;
75
+ }
76
+ /* line 38, ../sass/_tables.sass */
77
+ #leadin .table_search {
78
+ float: right;
79
+ padding: 10px 0;
80
+ padding-bottom: 9px;
81
+ }
82
+ /* line 44, ../sass/_tables.sass */
83
+ #leadin table .leadin-contact-avatar {
84
+ float: left;
85
+ }
86
+ /* line 48, ../sass/_tables.sass */
87
+ #leadin.pre-mp6 .table_search {
88
+ float: right;
89
+ padding: 12px 0;
90
+ padding-bottom: 11px;
91
+ }
92
+ /* line 53, ../sass/_tables.sass */
93
+ #leadin.pre-mp6 table {
94
+ background-color: white;
95
+ border-color: #dedede;
96
+ }
97
+ /* line 59, ../sass/_tables.sass */
98
+ #leadin.pre-mp6 table tr.alternate {
99
+ background-color: white;
100
+ }
101
+ /* line 62, ../sass/_tables.sass */
102
+ #leadin.pre-mp6 table th, #leadin.pre-mp6 table td {
103
+ border-top: 0;
104
+ padding: 12px 6px 11px;
105
+ }
106
+ /* line 66, ../sass/_tables.sass */
107
+ #leadin.pre-mp6 table th a, #leadin.pre-mp6 table td a {
108
+ padding: 0;
109
+ }
110
+ /* line 69, ../sass/_tables.sass */
111
+ #leadin.pre-mp6 table th[scope="col"] {
112
+ background: #eeeeee;
113
+ font-family: sans-serif;
114
+ font-size: 12px;
115
+ text-shadow: none;
116
+ }
117
+ /* line 75, ../sass/_tables.sass */
118
+ #leadin.pre-mp6 table td {
119
+ border-color: #dedede;
120
+ line-height: 18px;
121
+ font-size: 14px;
122
+ }
123
+ /* line 80, ../sass/_tables.sass */
124
+ #leadin.pre-mp6 table td .row-actions {
125
+ float: left;
126
+ }
127
+
128
+ /* line 9, ../sass/leadin-admin.sass */
129
+ #leadin {
130
+ padding-right: 20px;
131
+ }
132
+ @media screen and (min-width: 500px) {
133
+ /* line 9, ../sass/leadin-admin.sass */
134
+ #leadin {
135
+ padding-right: 10px;
136
+ }
137
+ }
138
+ /* line 15, ../sass/leadin-admin.sass */
139
+ #leadin label {
140
+ cursor: default;
141
+ }
142
+ /* line 18, ../sass/leadin-admin.sass */
143
+ #leadin .col-wrap {
144
+ padding: 0 14px 0 0;
145
+ }
146
+ /* line 21, ../sass/leadin-admin.sass */
147
+ #leadin .metabox-holder {
148
+ *zoom: 1;
149
+ }
150
+ /* line 38, ../../../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
151
+ #leadin .metabox-holder:after {
152
+ content: "";
153
+ display: table;
154
+ clear: both;
155
+ }
156
+ /* line 24, ../sass/leadin-admin.sass */
157
+ #leadin #leadin-footer {
158
+ *zoom: 1;
159
+ clear: both;
160
+ margin-top: 48px;
161
+ color: #999999;
162
+ border-top: 1px solid #dedede;
163
+ }
164
+ /* line 38, ../../../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
165
+ #leadin #leadin-footer:after {
166
+ content: "";
167
+ display: table;
168
+ clear: both;
169
+ }
170
+ /* line 31, ../sass/leadin-admin.sass */
171
+ #leadin #leadin-footer .support .sharing {
172
+ height: 18px;
173
+ text-align: left;
174
+ }
175
+ @media screen and (min-width: 500px) {
176
+ /* line 36, ../sass/leadin-admin.sass */
177
+ #leadin #leadin-footer .support, #leadin #leadin-footer .version, #leadin #leadin-footer .sharing {
178
+ width: 50%;
179
+ float: left;
180
+ }
181
+ /* line 40, ../sass/leadin-admin.sass */
182
+ #leadin #leadin-footer .sharing {
183
+ text-align: right;
184
+ }
185
+ }
186
+
187
+ /* line 45, ../sass/leadin-admin.sass */
188
+ #wp-admin-bar-leadin-admin-menu img {
189
+ height: 16px;
190
+ width: 16px;
191
+ opacity: 0.6;
192
+ }
193
+
194
+ /* line 1, ../sass/_settings_page.sass */
195
+ #icon-leadin {
196
+ background: url("../../images/leadin-icon-32x32.png") top center no-repeat;
197
+ }
198
+
199
+ /* line 4, ../sass/_settings_page.sass */
200
+ .help-notification {
201
+ background: #d9edf7;
202
+ border: 1px solid #bce8f1;
203
+ padding: 10px;
204
+ -webkit-border-radius: 3px;
205
+ -moz-border-radius: 3px;
206
+ border-radius: 3px;
207
+ }
208
+
209
+ /* line 12, ../sass/_settings_page.sass */
210
+ .toplevel_page_leadin_contacts .wp-menu-image img {
211
+ width: 16px;
212
+ height: 16px;
213
+ }
214
+
215
+ /* line 16, ../sass/_settings_page.sass */
216
+ .leadin-contact-avatar {
217
+ margin-right: 10px;
218
+ }
219
+
220
+ /* line 19, ../sass/_settings_page.sass */
221
+ .steps {
222
+ margin: 48px auto 0;
223
+ text-align: center;
224
+ max-width: 600px;
225
+ }
226
+ /* line 24, ../sass/_settings_page.sass */
227
+ .steps h3, .steps p {
228
+ margin: 0;
229
+ }
230
+ /* line 27, ../sass/_settings_page.sass */
231
+ .steps .step-names {
232
+ margin: 0;
233
+ *zoom: 1;
234
+ }
235
+ /* line 38, ../../../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
236
+ .steps .step-names:after {
237
+ content: "";
238
+ display: table;
239
+ clear: both;
240
+ }
241
+ /* line 31, ../sass/_settings_page.sass */
242
+ .steps .step-names .step-name {
243
+ color: #cccccc;
244
+ display: list-item;
245
+ float: left;
246
+ width: 33%;
247
+ margin: 0;
248
+ padding-bottom: 18px;
249
+ font-size: 16px;
250
+ list-style: decimal inside none;
251
+ }
252
+ /* line 41, ../sass/_settings_page.sass */
253
+ .steps .step-names .step-name.active {
254
+ color: #1f7d71;
255
+ background-image: url(../../images/triangle.png);
256
+ background-position: bottom center;
257
+ background-repeat: no-repeat;
258
+ }
259
+ /* line 47, ../sass/_settings_page.sass */
260
+ .steps .step-names .step-name.completed {
261
+ list-style-image: url(../../images/checkmark.png);
262
+ }
263
+ /* line 50, ../sass/_settings_page.sass */
264
+ .steps .step-content {
265
+ margin: 0;
266
+ }
267
+ /* line 53, ../sass/_settings_page.sass */
268
+ .steps .step-content .description {
269
+ margin: 10px 0 20px;
270
+ display: block;
271
+ }
272
+ /* line 57, ../sass/_settings_page.sass */
273
+ .steps .step {
274
+ display: none;
275
+ padding: 18px;
276
+ background-color: #d3eeeb;
277
+ border: 2px solid #22aa99;
278
+ -webkit-border-radius: 5px;
279
+ -moz-border-radius: 5px;
280
+ -ms-border-radius: 5px;
281
+ -o-border-radius: 5px;
282
+ border-radius: 5px;
283
+ color: #1f7d71;
284
+ }
285
+ /* line 65, ../sass/_settings_page.sass */
286
+ .steps .step h2 {
287
+ color: #1f7d71;
288
+ margin-top: 0;
289
+ margin-bottom: 18px;
290
+ }
291
+ /* line 71, ../sass/_settings_page.sass */
292
+ .steps .step ol {
293
+ text-align: left;
294
+ margin-bottom: 18px;
295
+ }
296
+ /* line 75, ../sass/_settings_page.sass */
297
+ .steps .step label {
298
+ text-align: right;
299
+ }
300
+ /* line 78, ../sass/_settings_page.sass */
301
+ .steps .step.active {
302
+ display: block;
303
+ }
304
+ /* line 82, ../sass/_settings_page.sass */
305
+ .steps .step .form-table th {
306
+ display: none;
307
+ }
308
+ /* line 85, ../sass/_settings_page.sass */
309
+ .steps .step .form-table td {
310
+ text-align: center;
311
+ width: auto;
312
+ display: block;
313
+ }
314
+ /* line 90, ../sass/_settings_page.sass */
315
+ .steps .step .form-table input {
316
+ width: 100%;
317
+ font-size: 16px;
318
+ line-height: 1.5;
319
+ padding: 7px 10px;
320
+ display: block;
321
+ }
322
+
323
+ /* line 5, ../sass/_contacts_list_page.sass */
324
+ #leadin-contacts th#source {
325
+ width: 20%;
326
+ }
327
+ /* line 8, ../sass/_contacts_list_page.sass */
328
+ #leadin-contacts th#visits, #leadin-contacts th#submissions {
329
+ width: 8%;
330
+ }
331
+ /* line 11, ../sass/_contacts_list_page.sass */
332
+ #leadin-contacts th#status, #leadin-contacts th#last_visit, #leadin-contacts th#date, #leadin-contacts th#pageviews {
333
+ width: 10%;
334
+ }
335
+
336
+ /* line 3, ../sass/_contact_detail_page.sass */
337
+ #leadin .header-wrap {
338
+ *zoom: 1;
339
+ padding: 9px 15px 4px 0;
340
+ }
341
+ /* line 38, ../../../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
342
+ #leadin .header-wrap:after {
343
+ content: "";
344
+ display: table;
345
+ clear: both;
346
+ }
347
+ /* line 7, ../sass/_contact_detail_page.sass */
348
+ #leadin .header-wrap h1, #leadin .header-wrap img {
349
+ float: left;
350
+ }
351
+ /* line 10, ../sass/_contact_detail_page.sass */
352
+ #leadin .header-wrap h1 {
353
+ padding: 0 0 0 10px;
354
+ margin: 0;
355
+ }
356
+ /* line 14, ../sass/_contact_detail_page.sass */
357
+ #leadin .contact-name {
358
+ line-height: 40px;
359
+ }
360
+ /* line 19, ../sass/_contact_detail_page.sass */
361
+ #leadin .contact-info label {
362
+ font-weight: bold;
363
+ line-height: 1;
364
+ cursor: default;
365
+ }
366
+ /* line 24, ../sass/_contact_detail_page.sass */
367
+ #leadin .contact-history {
368
+ padding-left: 20px;
369
+ margin-left: 20px;
370
+ border-left: 2px solid #dedede;
371
+ }
372
+ /* line 31, ../sass/_contact_detail_page.sass */
373
+ #leadin .contact-history .session + .session {
374
+ margin-top: 30px;
375
+ }
376
+ /* line 34, ../sass/_contact_detail_page.sass */
377
+ #leadin .contact-history .session-date {
378
+ position: relative;
379
+ }
380
+ /* line 37, ../sass/_contact_detail_page.sass */
381
+ #leadin .contact-history .session-date:before {
382
+ content: "\2022";
383
+ font-size: 32px;
384
+ line-height: 0;
385
+ height: 31px;
386
+ width: 31px;
387
+ position: absolute;
388
+ left: -27px;
389
+ top: 9px;
390
+ color: #dedede;
391
+ }
392
+ /* line 48, ../sass/_contact_detail_page.sass */
393
+ #leadin .contact-history .events {
394
+ background-color: white;
395
+ border: 1px solid #dedede;
396
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
397
+ -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
398
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
399
+ }
400
+ /* line 53, ../sass/_contact_detail_page.sass */
401
+ #leadin .contact-history .event {
402
+ margin: 0;
403
+ padding: 10px 20px;
404
+ border-bottom: 1px solid #dedede;
405
+ border-left: 4px solid;
406
+ }
407
+ /* line 60, ../sass/_contact_detail_page.sass */
408
+ #leadin .contact-history .event:first-child {
409
+ border-top: 0;
410
+ }
411
+ /* line 63, ../sass/_contact_detail_page.sass */
412
+ #leadin .contact-history .event.pageview {
413
+ border-left-color: #2288cc;
414
+ color: #2288cc;
415
+ }
416
+ /* line 67, ../sass/_contact_detail_page.sass */
417
+ #leadin .contact-history .event.form-submission {
418
+ border-left-color: #f66000;
419
+ color: #f66000;
420
+ }
421
+ /* line 71, ../sass/_contact_detail_page.sass */
422
+ #leadin .contact-history .event.source {
423
+ border-left-color: #99aa1f;
424
+ color: #99aa1f;
425
+ }
426
+ /* line 75, ../sass/_contact_detail_page.sass */
427
+ #leadin .contact-history .event-title {
428
+ margin: 0;
429
+ font-size: 13px;
430
+ font-weight: 600;
431
+ }
432
+ /* line 80, ../sass/_contact_detail_page.sass */
433
+ #leadin .contact-history .event-time-range {
434
+ float: right;
435
+ font-weight: 400;
436
+ font-size: 0.85em;
437
+ color: #999999;
438
+ }
439
+ /* line 86, ../sass/_contact_detail_page.sass */
440
+ #leadin .contact-history .event-detail {
441
+ margin-top: 20px;
442
+ color: #444444;
443
+ }
444
+ /* line 90, ../sass/_contact_detail_page.sass */
445
+ #leadin .contact-history .event-detail li + li {
446
+ padding-top: 6px;
447
+ border-top: 1px solid #eeeeee;
448
+ }
449
+ /* line 95, ../sass/_contact_detail_page.sass */
450
+ #leadin .contact-history .visit-source p {
451
+ margin: 0;
452
+ color: #1f6696;
453
+ }
454
+ /* line 99, ../sass/_contact_detail_page.sass */
455
+ #leadin .contact-history .pageview-url {
456
+ margin: 0;
457
+ color: #cccccc;
458
+ }
459
+ /* line 103, ../sass/_contact_detail_page.sass */
460
+ #leadin .contact-history .field-label {
461
+ text-transform: uppercase;
462
+ letter-spacing: 0.05em;
463
+ color: #999999;
464
+ margin-bottom: 6px;
465
+ font-size: 0.9em;
466
+ }
467
+ /* line 110, ../sass/_contact_detail_page.sass */
468
+ #leadin .contact-history .field-value {
469
+ margin: 0;
470
+ }
471
+ /* line 115, ../sass/_contact_detail_page.sass */
472
+ #leadin.pre-mp6 .events {
473
+ background-color: #f9f9f9;
474
+ }
475
+
476
+ /* line 1, ../sass/_powerups_page.sass */
477
+ .powerup-list {
478
+ margin: 0;
479
+ }
480
+ /* line 4, ../sass/_powerups_page.sass */
481
+ .powerup-list .powerup {
482
+ border: 2px solid;
483
+ width: 20%;
484
+ min-width: 250px;
485
+ float: left;
486
+ margin: 20px;
487
+ padding: 15px;
488
+ background-color: #f9f9f9;
489
+ border-color: #cccccc;
490
+ text-align: center;
491
+ -webkit-border-radius: 10px;
492
+ -moz-border-radius: 10px;
493
+ -ms-border-radius: 10px;
494
+ -o-border-radius: 10px;
495
+ border-radius: 10px;
496
+ }
497
+ /* line 16, ../sass/_powerups_page.sass */
498
+ .powerup-list .powerup h2, .powerup-list .powerup p, .powerup-list .powerup img {
499
+ margin: 0;
500
+ padding: 0;
501
+ color: #666666;
502
+ margin-bottom: 15px;
503
+ }
504
+ /* line 22, ../sass/_powerups_page.sass */
505
+ .powerup-list .powerup.activated {
506
+ background-color: #d3eeeb;
507
+ border-color: #1f7d71;
508
+ }
509
+ /* line 26, ../sass/_powerups_page.sass */
510
+ .powerup-list .powerup.activated h2, .powerup-list .powerup.activated p {
511
+ color: #1f7d71;
512
+ }
admin/inc/class-leadin-contact.php ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //=============================================
3
+ // LI_Contact Class
4
+ //=============================================
5
+ class LI_Contact {
6
+
7
+ /**
8
+ * Variables
9
+ */
10
+ var $hashkey;
11
+ var $history;
12
+
13
+ /**
14
+ * Class constructor
15
+ */
16
+ function LI_Contact () {
17
+
18
+ }
19
+
20
+ /**
21
+ * Gets hashkey from lead id
22
+ *
23
+ * @param int
24
+ * @return string
25
+ */
26
+ function set_hashkey_by_id ( $lead_id ) {
27
+ global $wpdb;
28
+ $q = $wpdb->prepare("SELECT hashkey FROM li_leads WHERE lead_id = %d", $lead_id);
29
+ $this->hashkey = $wpdb->get_var($q);
30
+
31
+ return $this->hashkey;
32
+ }
33
+
34
+ /**
35
+ * Gets contact history from the database (pageviews, form submissions, and lead details)
36
+ *
37
+ * @param string
38
+ * @return object $history (pageviews_by_session, submission, lead)
39
+ */
40
+ function get_contact_history () {
41
+ global $wpdb;
42
+
43
+ // Get the contact details
44
+ $q = $wpdb->prepare("
45
+ SELECT
46
+ DATE_FORMAT(lead_date, %s) AS lead_date,
47
+ lead_ip,
48
+ lead_source,
49
+ lead_email,
50
+ lead_status
51
+ FROM
52
+ li_leads
53
+ WHERE hashkey LIKE %s", '%b %D %l:%i%p', $this->hashkey);
54
+
55
+ $lead = $wpdb->get_row($q);
56
+
57
+ // Get all page views for the contact
58
+ $q = $wpdb->prepare("
59
+ SELECT
60
+ pageview_id,
61
+ pageview_date AS event_date,
62
+ DATE_FORMAT(pageview_date, %s) AS pageview_day,
63
+ DATE_FORMAT(pageview_date, %s) AS pageview_date,
64
+ lead_hashkey, pageview_title, pageview_url, pageview_source, pageview_session_start
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);
71
+
72
+ // Get all submissions for the contact
73
+ $q = $wpdb->prepare("
74
+ SELECT
75
+ form_date AS event_date,
76
+ DATE_FORMAT(form_date, %s) AS form_date,
77
+ form_page_title,
78
+ form_page_url,
79
+ form_fields,
80
+ form_type
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);
87
+
88
+ // Merge the page views array and submissions array and reorder by date
89
+ $events_array = array_merge($pageviews, $submissions);
90
+ usort($events_array, array('LI_Contact','sort_by_event_date'));
91
+
92
+ $sessions = array();
93
+ $cur_array = '0';
94
+ $first_iteration = TRUE;
95
+ $count = 0;
96
+ $cur_event = 0;
97
+ $prev_form_event = FALSE;
98
+ $total_visits = 0;
99
+ $total_pageviews = 0;
100
+ $total_submissions = 0;
101
+
102
+ foreach ( $events_array as $event_name => $event )
103
+ {
104
+ // Create a new session array if pageview started a new session
105
+ if ( (isset($event['pageview_session_start']) && $event['pageview_session_start'] ) || $first_iteration )
106
+ {
107
+ $cur_array = $count;
108
+ $sessions['session_' . $cur_array] = array();
109
+ $sessions['session_' . $cur_array]['session_date'] = $event['event_date'];
110
+ $sessions['session_' . $cur_array]['events'] = array();
111
+
112
+ if ( $first_iteration )
113
+ $first_iteration = FALSE;
114
+
115
+ $cur_event = $count;
116
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event] = array();
117
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_type'] = 'pageview';
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
125
+ $lead->last_visit = $event['event_date'];
126
+
127
+ // Used for $lead->total_visits
128
+ $total_visits++;
129
+ }
130
+
131
+ // Pageview activity
132
+ if ( !isset($event['form_fields']) )
133
+ {
134
+ $cur_event = $count;
135
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event] = array();
136
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_type'] = 'pageview';
137
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_date'] = $event['event_date'];
138
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['activities'][] = $event;
139
+ $total_pageviews++;
140
+ }
141
+ else
142
+ {
143
+ $cur_event = $count;
144
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event] = array();
145
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_type'] = 'form';
146
+ $sessions['session_' . $cur_array]['events']['event_' . $cur_event]['event_date'] = $event['event_date'];
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
154
+ $lead->last_submission = $event['event_date'];
155
+
156
+ // Used for $lead->total_submissions
157
+ $total_submissions++;
158
+ }
159
+
160
+ $count++;
161
+ }
162
+
163
+ $lead->lead_status = $this->frontend_lead_status($lead->lead_status);
164
+ $lead->total_visits = $total_visits;
165
+ $lead->total_pageviews = $total_pageviews;
166
+ $lead->total_submissions = $total_submissions;
167
+
168
+ $this->history = (object)NULL;
169
+ $this->history->sessions = $sessions;
170
+ $this->history->lead = $lead;
171
+
172
+ return stripslashes_deep($this->history);
173
+ }
174
+
175
+ /**
176
+ * usort helper function to sort array by event date
177
+ *
178
+ * @param string
179
+ * @return array
180
+ */
181
+ function sort_by_event_date ( $a, $b ) {
182
+ return $a['event_date'] > $b['event_date'];
183
+ }
184
+
185
+ /**
186
+ * Normalizes li_leads.lead_status for front end display
187
+ *
188
+ * @param string
189
+ * @return string
190
+ */
191
+ function frontend_lead_status ( $lead_status = 'lead' ) {
192
+ if ( $lead_status == 'lead' )
193
+ return 'Lead';
194
+ else if ( $lead_status == 'comment' )
195
+ return 'Commenter';
196
+ else
197
+ return 'Subscriber';
198
+ }
199
+ }
200
+ ?>
admin/inc/class-leadin-list-table.php ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ //=============================================
4
+ // Include Needed Files
5
+ //=============================================
6
+
7
+ if ( !class_exists('WP_List_Table') )
8
+ require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
9
+
10
+ require_once(LEADIN_PLUGIN_DIR . '/inc/leadin-functions.php');
11
+
12
+ //=============================================
13
+ // LI_List_Table Class
14
+ //=============================================
15
+ class LI_List_Table extends WP_List_Table {
16
+
17
+ /**
18
+ * Variables
19
+ */
20
+ public $data = array();
21
+
22
+ /**
23
+ * Class constructor
24
+ */
25
+ function __construct ()
26
+ {
27
+ global $status, $page;
28
+
29
+ //Set parent defaults
30
+ parent::__construct( array(
31
+ 'singular' => 'contact',
32
+ 'plural' => 'contacts',
33
+ 'ajax' => false
34
+ ));
35
+ }
36
+
37
+ /**
38
+ * Prints text for no rows found in table
39
+ */
40
+ function no_items ()
41
+ {
42
+ _e('No contacts found.');
43
+ }
44
+
45
+ /**
46
+ * Prints values for columns for which no column function has been defined
47
+ *
48
+ * @param object
49
+ * @param string
50
+ * @return * item value's type
51
+ */
52
+ function column_default ( $item, $column_name )
53
+ {
54
+ switch ( $column_name )
55
+ {
56
+ case 'email':
57
+
58
+ case 'status':
59
+ return $item[$column_name];
60
+ case 'date':
61
+ return $item[$column_name];
62
+ case 'last_visit':
63
+ return $item[$column_name];
64
+ case 'submissions':
65
+ return $item[$column_name];
66
+ case 'pageviews':
67
+ return $item[$column_name];
68
+ case 'visits':
69
+ return $item[$column_name];
70
+ case 'source':
71
+ return $item[$column_name];
72
+ default:
73
+ return print_r($item,true);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Prints text for email column
79
+ *
80
+ * @param object
81
+ * @return string
82
+ */
83
+ function column_email ( $item )
84
+ {
85
+ //Build row actions
86
+ $actions = array(
87
+ 'view' => sprintf('<div style="clear:both;"></div><a href="?page=%s&action=%s&lead=%s">View</a>',$_REQUEST['page'],'view',$item['ID']),
88
+ 'delete' => sprintf('<a href="?page=%s&action=%s&lead=%s">Delete</a>',$_REQUEST['page'],'delete',$item['ID'])
89
+ );
90
+
91
+ //Return the title contents
92
+ return sprintf('%1$s<br/>%2$s',
93
+ /*$1%s*/ $item['email'],
94
+ /*$2%s*/ $this->row_actions($actions)
95
+ );
96
+ }
97
+
98
+ /**
99
+ * Prints checkbox column
100
+ *
101
+ * @param object
102
+ * @return string
103
+ */
104
+ function column_cb ( $item )
105
+ {
106
+ return sprintf(
107
+ '<input type="checkbox" name="%1$s[]" value="%2$s" />',
108
+ /*$1%s*/ $this->_args['singular'],
109
+ /*$2%s*/ $item['ID']
110
+ );
111
+ }
112
+
113
+ /**
114
+ * Get all the columns for the list table
115
+ *
116
+ * @param object
117
+ * @param string
118
+ * @return array associative array of columns
119
+ */
120
+ function get_columns ()
121
+ {
122
+ $columns = array(
123
+ 'cb' => '<input type="checkbox" />',
124
+ 'email' => 'Email',
125
+ 'source' => 'Original source',
126
+ 'status' => 'Status',
127
+ 'visits' => 'Visits',
128
+ 'pageviews' => 'Page views',
129
+ 'submissions' => 'Forms',
130
+ 'last_visit' => 'Last visit',
131
+ 'date' => 'Created on'
132
+ );
133
+ return $columns;
134
+ }
135
+
136
+ /**
137
+ * Defines sortable columns for table
138
+ *
139
+ * @param object
140
+ * @param string
141
+ * @return array associative array of columns
142
+ */
143
+ function get_sortable_columns ()
144
+ {
145
+ $sortable_columns = array(
146
+ 'email' => array('email',false), //true means it's already sorted
147
+ 'status' => array('status',false),
148
+ 'pageviews' => array('pageviews',false),
149
+ 'visits' => array('visits',false),
150
+ 'submissions' => array('submissions',false),
151
+ 'date' => array('date',true),
152
+ 'last_visit' => array('last_visit',false),
153
+ 'source' => array('source',false)
154
+ );
155
+ return $sortable_columns;
156
+ }
157
+
158
+ /**
159
+ * Get the bulk actions
160
+ *
161
+ * @return array associative array of actions
162
+ */
163
+ function get_bulk_actions ()
164
+ {
165
+ $actions = array(
166
+ 'delete' => 'Delete'
167
+ );
168
+ return $actions;
169
+ }
170
+
171
+ /**
172
+ * Process bulk actions for deleting
173
+ */
174
+ function process_bulk_action ()
175
+ {
176
+ global $wpdb;
177
+ $ids_to_delete = '';
178
+ $hashes_to_delete = '';
179
+
180
+ if ( isset ($_GET['contact']) )
181
+ {
182
+ for ( $i = 0; $i < count($_GET['contact']); $i++ )
183
+ {
184
+ $ids_to_delete .= $_GET['contact'][$i];;
185
+
186
+ if ( $i != (count($_GET['contact'])-1) )
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
+ /**
219
+ * Get the leads for the contacts table based on $GET_['contact_type'] or $_GET['s'] (search)
220
+ *
221
+ * @return array associative array of all contacts
222
+ */
223
+ function get_leads ()
224
+ {
225
+ global $wpdb;
226
+
227
+ $mysql_search_filter = '';
228
+
229
+ if ( isset($_GET['s']) )
230
+ {
231
+ $search_query = $_GET['s'];
232
+ $mysql_search_filter = $wpdb->prepare(" AND ( l.lead_email LIKE '%%%s%%' OR l.lead_source LIKE '%%%s%%' ) ", like_escape($search_query), like_escape($search_query));
233
+ }
234
+
235
+ if ( isset($_GET['contact_type']) )
236
+ {
237
+ $mysql_contact_type_filter = $wpdb->prepare("AND l.lead_status = %s ", $_GET['contact_type']);
238
+ }
239
+ else
240
+ {
241
+ $mysql_contact_type_filter = " AND ( l.lead_status = 'lead' OR l.lead_status = 'comment' OR l.lead_status = 'subscribe') ";
242
+ }
243
+
244
+ $q = $wpdb->prepare("
245
+ SELECT
246
+ l.lead_id AS lead_id,
247
+ LOWER(DATE_FORMAT(l.lead_date, %s)) AS lead_date, l.lead_ip, l.lead_source, l.lead_email, l.lead_status, l.hashkey,
248
+ COUNT(DISTINCT s.form_id) AS lead_form_submissions,
249
+ COUNT(DISTINCT p.pageview_id) AS lead_pageviews,
250
+ LOWER(DATE_FORMAT(MAX(p.pageview_date), %s)) AS last_visit
251
+ FROM
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 : "" );
259
+ $q .= " GROUP BY l.lead_email";
260
+
261
+ $leads = $wpdb->get_results($q);
262
+
263
+ $all_leads = array();
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
+
271
+ $lead_status = 'Lead';
272
+
273
+ if ( $lead->lead_status == 'subscribe' )
274
+ $lead_status = 'Subscriber';
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),
281
+ 'status' => $lead_status,
282
+ 'visits' => ( !$lead->lead_visits ? 1 : $lead->lead_visits ),
283
+ 'submissions' => $lead->lead_form_submissions,
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);
291
+ }
292
+
293
+ return $all_leads;
294
+ }
295
+
296
+ /**
297
+ * Gets the total number of contacts, comments and subscribers for above the table
298
+ */
299
+ function get_contact_type_totals ()
300
+ {
301
+ global $wpdb;
302
+ // @TODO Need to select distinct emails
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;
316
+ }
317
+
318
+ /**
319
+ * Gets the current view based off $_GET['contact_type']
320
+ *
321
+ * @return string
322
+ */
323
+ function get_view ()
324
+ {
325
+ $current = ( !empty($_GET['contact_type']) ? html_entity_decode($_GET['contact_type']) : 'all' );
326
+ return $current;
327
+ }
328
+
329
+ /**
330
+ * Get the view menus above the contacts table
331
+ *
332
+ * @return string
333
+ */
334
+ function get_views ()
335
+ {
336
+ $views = array();
337
+ $totals = $this->get_contact_type_totals();
338
+
339
+ $current = ( !empty($_GET['contact_type']) ? html_entity_decode($_GET['contact_type']) : 'all' );
340
+
341
+ // All link
342
+ $class = ( $current == 'all' ? ' class="current"' :'' );
343
+ $all_url = remove_query_arg('contact_type');
344
+ $views['all'] = "<a href='{$all_url }' {$class} >" . ( $totals->total_leads + $totals->total_comments + $totals->total_subscribes ) . " total</a>";
345
+
346
+ // Leads link
347
+ $leads_url = add_query_arg('contact_type','lead');
348
+ $class = ( $current == 'lead' ? ' class="current"' :'' );
349
+ $views['contacts'] = "<a href='{$leads_url}' {$class} >" . leadin_single_plural_label($totals->total_leads, 'lead', 'leads') . "</a>";
350
+
351
+ // Commenters link
352
+ $comments_url = add_query_arg('contact_type','comment');
353
+ $class = ( $current == 'comment' ? ' class="current"' :'' );
354
+ $views['commenters'] = "<a href='{$comments_url}' {$class} >" . leadin_single_plural_label($totals->total_comments, 'commenter', 'commenters') . "</a>";
355
+
356
+ // Commenters link
357
+ $subscribers_url = add_query_arg('contact_type','subscribe');
358
+ $class = ( $current == 'subscribe' ? ' class="current"' :'' );
359
+ $views['subscribe'] = "<a href='{$subscribers_url}' {$class} >" . leadin_single_plural_label($totals->total_subscribes, 'subscriber', 'subscribers') . "</a>";
360
+
361
+ return $views;
362
+ }
363
+
364
+ /**
365
+ * Prints contacts menu above contacts table
366
+ */
367
+ function views ()
368
+ {
369
+ $views = $this->get_views();
370
+ $views = apply_filters( 'views_' . $this->screen->id, $views );
371
+
372
+ $current_view = $this->get_view();
373
+
374
+ if ( $current_view == 'lead' )
375
+ $view_label = 'Leads';
376
+ else if ( $current_view == 'comment' )
377
+ $view_label = 'Commenters';
378
+ else if ( $current_view == 'subscribe' )
379
+ $view_label = 'Subscribers';
380
+ else
381
+ $view_label = 'Contacts';
382
+
383
+ if ( empty( $views ) )
384
+ return;
385
+
386
+ echo "<div class='top_table_controls'>\n";
387
+
388
+ echo "<ul class='table_segment_picker'>\n";
389
+ foreach ( $views as $class => $view ) {
390
+ $views[ $class ] = "\t<li class='$class'>$view";
391
+ }
392
+ echo implode( "</li>\n", $views ) . "</li>\n";
393
+ echo "</ul>";
394
+
395
+ echo "<span class='table_search'>\n";
396
+ echo "<label class='screen-reader-text' for='post-search-input'>Search Contacts:</label>";
397
+ echo "<input type='search' id='leadin-contact-search-input' name='s' value='" . print_submission_val('s') . "'/>";
398
+ if ( isset ($_GET['contact_type']) ) {
399
+ echo "<input type='hidden' name='contact_type' value='" . print_submission_val('contact_type') . "'/>";
400
+ }
401
+ echo "<input type='submit' name='' id='leadin-search-submit' class='button' value='Search " . $view_label . "'>";
402
+ echo "</span>";
403
+
404
+ echo "</div>";
405
+ }
406
+
407
+ /**
408
+ * Gets + prepares the contacts for the list table
409
+ */
410
+ function prepare_items ()
411
+ {
412
+ global $wpdb;
413
+
414
+ $per_page = 10;
415
+
416
+ $columns = $this->get_columns();
417
+ $hidden = array();
418
+ $sortable = $this->get_sortable_columns();
419
+ $this->_column_headers = array($columns, $hidden, $sortable);
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] )
432
+ $result = 0;
433
+ else if ( $a[$orderby] < $b[$orderby] )
434
+ $result = -1;
435
+ else
436
+ $result = 1;
437
+
438
+ return ( $order === 'asc' ? $result : -$result );
439
+ }
440
+
441
+ usort($this->data, 'usort_reorder');
442
+
443
+ $current_page = $this->get_pagenum();
444
+ $total_items = count($this->data);
445
+ $this->data = array_slice($this->data, (($current_page-1)*$per_page), $per_page);
446
+ $this->items = $this->data;
447
+
448
+ $this->set_pagination_args( array(
449
+ 'total_items' => $total_items,
450
+ 'per_page' => $per_page,
451
+ 'total_pages' => ceil($total_items/$per_page)
452
+ ) );
453
+ }
454
+
455
+ }
admin/inc/class-leadin-pointers.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( !defined('LEADIN_PLUGIN_VERSION') )
4
+ {
5
+ header('HTTP/1.0 403 Forbidden');
6
+ die;
7
+ }
8
+
9
+ /**
10
+ * This class handles the pointers used in the introduction tour.
11
+ *
12
+ * @todo Add an introdutory pointer on the edit post page too.
13
+ */
14
+ class LI_Pointers {
15
+
16
+ /**
17
+ * Class constructor.
18
+ */
19
+ function __construct ()
20
+ {
21
+ //=============================================
22
+ // Hooks & Filters
23
+ //=============================================
24
+
25
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
26
+ }
27
+
28
+ /**
29
+ * Enqueue styles and scripts needed for the pointers.
30
+ */
31
+ function enqueue ()
32
+ {
33
+ if ( !current_user_can( 'manage_options' ) )
34
+ return;
35
+
36
+ $options = get_option('leadin_options');
37
+
38
+ if ( !isset($options['ignore_settings_popup']) || !$options['ignore_settings_popup'] )
39
+ {
40
+ wp_enqueue_style( 'wp-pointer' );
41
+ wp_enqueue_script( 'jquery-ui' );
42
+ wp_enqueue_script( 'wp-pointer' );
43
+ wp_enqueue_script( 'utils' );
44
+
45
+ add_action('admin_print_footer_scripts', array( $this, 'li_settings_popup'));
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Shows a popup that asks for permission to allow tracking.
51
+ */
52
+ function li_settings_popup() {
53
+ $id = '#toplevel_page_leadin_contacts';
54
+ $nonce = wp_create_nonce( 'wpseo_activate_tracking' );
55
+
56
+ $content = '<h3>' . __( 'Finish setting up LeadIn', 'leadin' ) . '</h3>';
57
+ $content .= '<p>' . __( 'You\'ve just installed LeadIn. Visit your settings page to make sure LeadIn is properly setup.', 'leadin' ) . '</p>';
58
+ $opt_arr = array(
59
+ 'content' => $content,
60
+ 'position' => array( 'edge' => 'left', 'align' => 'center' )
61
+ );
62
+
63
+ $function2 = 'li_redirect_to_settings()';
64
+
65
+ $this->print_scripts($id, $opt_arr, 'Go to settings', FALSE, '', $function2);
66
+ }
67
+
68
+ /**
69
+ * Prints the pointer script
70
+ *
71
+ * @param string $selector The CSS selector the pointer is attached to.
72
+ * @param array $options The options for the pointer.
73
+ * @param string $button1 Text for button 1
74
+ * @param string|bool $button2 Text for button 2 (or false to not show it, defaults to false)
75
+ * @param string $button2_function The JavaScript function to attach to button 2
76
+ * @param string $button1_function The JavaScript function to attach to button 1
77
+ */
78
+ function print_scripts( $selector, $options, $button1, $button2 = FALSE, $button2_function = '', $button1_function = '' )
79
+ {
80
+ ?>
81
+ <script type="text/javascript">
82
+ //<![CDATA[
83
+ (function ($) {
84
+
85
+ var li_pointer_options = <?php echo json_encode( $options ); ?>, setup;
86
+
87
+ function li_redirect_to_settings() {
88
+ window.location.href = "<?php echo get_bloginfo('wpurl'); ?>/wp-admin/admin.php?page=leadin_settings";
89
+ }
90
+
91
+ li_pointer_options = $.extend(li_pointer_options, {
92
+ buttons: function (event, t) {
93
+ button = jQuery('<a id="pointer-close" style="margin-left:5px" class="button-secondary">' + '<?php echo $button1; ?>' + '</a>');
94
+ button.bind('click.pointer', function () {
95
+ window.location.href = "<?php echo get_bloginfo('wpurl'); ?>/wp-admin/admin.php?page=leadin_settings";
96
+ //t.element.pointer('close');
97
+ });
98
+ return button;
99
+ },
100
+ close : function () {
101
+ }
102
+ });
103
+
104
+ setup = function () {
105
+ $('<?php echo $selector; ?>').pointer(li_pointer_options).pointer('open');
106
+ };
107
+
108
+ if (li_pointer_options.position && li_pointer_options.position.defer_loading)
109
+ $(window).bind('load.wp-pointers', setup);
110
+ else
111
+ $(document).ready(setup);
112
+ })(jQuery);
113
+ //]]>
114
+ </script>
115
+ <?php
116
+ }
117
+ }
admin/js/leadin-admin.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ jQuery(document).ready( function ( $ ) {
2
+
3
+ });
admin/leadin-admin.php ADDED
@@ -0,0 +1,503 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( !defined('LEADIN_PLUGIN_VERSION') )
4
+ {
5
+ header('HTTP/1.0 403 Forbidden');
6
+ die;
7
+ }
8
+
9
+ //=============================================
10
+ // Define Constants
11
+ //=============================================
12
+
13
+ if ( !defined('LEADIN_ADMIN_PATH') )
14
+ define('LEADIN_ADMIN_PATH', untrailingslashit(__FILE__));
15
+
16
+ //=============================================
17
+ // Include Needed Files
18
+ //=============================================
19
+
20
+ if ( !class_exists('LI_List_Table') )
21
+ require_once LEADIN_PLUGIN_DIR . '/admin/inc/class-leadin-list-table.php';
22
+
23
+ if ( !class_exists('LI_Contact') )
24
+ require_once LEADIN_PLUGIN_DIR . '/admin/inc/class-leadin-contact.php';
25
+
26
+ if ( !class_exists('LI_Pointers') )
27
+ require_once LEADIN_PLUGIN_DIR . '/admin/inc/class-leadin-pointers.php';
28
+
29
+ include_once(ABSPATH . 'wp-admin/includes/plugin.php');
30
+
31
+ //=============================================
32
+ // WPLeadInAdmin Class
33
+ //=============================================
34
+ class WPLeadInAdmin {
35
+
36
+ var $admin_power_ups;
37
+
38
+ /**
39
+ * Class constructor
40
+ */
41
+ function __construct ( $power_ups )
42
+ {
43
+ //=============================================
44
+ // Hooks & Filters
45
+ //=============================================
46
+
47
+ $this->admin_power_ups = $power_ups;
48
+
49
+ if( is_admin() )
50
+ {
51
+ add_action('admin_menu', array(&$this, 'leadin_add_menu_items'));
52
+ add_action('admin_init', array(&$this, 'leadin_build_settings_page'));
53
+ add_action('admin_print_styles', array(&$this, 'add_leadin_admin_styles'));
54
+ }
55
+ }
56
+
57
+ //=============================================
58
+ // Menus
59
+ //=============================================
60
+
61
+ /**
62
+ * Adds LeadIn menu to /wp-admin sidebar
63
+ */
64
+ function leadin_add_menu_items ()
65
+ {
66
+ global $submenu;
67
+ global $wp_version;
68
+
69
+ self::check_admin_action();
70
+
71
+ foreach ( $this->admin_power_ups as $power_up )
72
+ {
73
+ if ( $power_up->activated )
74
+ {
75
+ $power_up->admin_init();
76
+
77
+ if ( $power_up->menu_text == 'Contacts' )
78
+ add_menu_page('LeadIn', 'LeadIn', 'manage_categories', 'leadin_contacts', array($power_up, 'power_up_setup_callback'), LEADIN_PATH . '/images/' . ( $wp_version < 3.8 && !is_plugin_active('mp6/mp6.php') ? 'leadin-icon-32x32.png' : 'leadin-svg-icon.svg'));
79
+ else if ( $power_up->menu_text )
80
+ add_submenu_page('leadin_contacts', $power_up->menu_text, $power_up->menu_text, 'manage_categories', 'leadin_' . $power_up->menu_link, array($power_up, 'power_up_setup_callback'));
81
+ }
82
+ }
83
+
84
+ add_submenu_page('leadin_contacts', 'Settings', 'Settings', 'manage_categories', 'leadin_settings', array(&$this, 'leadin_plugin_options'));
85
+ add_submenu_page('leadin_contacts', 'Power-ups', 'Power-ups', 'manage_categories', 'leadin_power_ups', array(&$this, 'leadin_power_ups_page'));
86
+ $submenu['leadin_contacts'][0][0] = 'Contacts';
87
+
88
+ if ( !isset($_GET['page']) || $_GET['page'] != 'leadin_settings' )
89
+ {
90
+ $options = get_option('leadin_options');
91
+ if ( !isset($options['ignore_settings_popup']) || !$options['ignore_settings_popup'] )
92
+ $li_pointers = new LI_Pointers();
93
+ }
94
+ }
95
+
96
+ //=============================================
97
+ // Settings Page
98
+ //=============================================
99
+
100
+ /**
101
+ * Adds setting link for LeadIn to plugins management page
102
+ *
103
+ * @param array $links
104
+ * @return array
105
+ */
106
+ function leadin_plugin_settings_link ( $links )
107
+ {
108
+ $url = get_admin_url() . 'admin.php?page=leadin_settings';
109
+ $settings_link = '<a href="' . $url . '">Settings</a>';
110
+ array_unshift($links, $settings_link);
111
+ return $links;
112
+ }
113
+
114
+ /**
115
+ * Creates settings options
116
+ */
117
+ function leadin_build_settings_page ()
118
+ {
119
+ // Show the settings popup on all pages except the settings page
120
+ if( isset($_GET['settings-updated']) )
121
+ {
122
+ $options = get_option('leadin_options');
123
+
124
+ if ( !isset($options['onboarding_complete']) || !$options['onboarding_complete'] )
125
+ leadin_update_option('leadin_options', 'onboarding_complete', 1);
126
+
127
+ if ( !isset($options['ignore_settings_popup']) || !$options['ignore_settings_popup'] )
128
+ leadin_update_option('leadin_options', 'ignore_settings_popup', 1);
129
+ }
130
+
131
+ register_setting('leadin_settings_options', 'leadin_options', array($this, 'sanitize'));
132
+ add_settings_section('leadin_settings_section', 'Visitor Tracking', array($this, 'leadin_options_section_heading'), LEADIN_ADMIN_PATH);
133
+ add_settings_field('li_email', 'Email', array($this, 'li_email_callback'), LEADIN_ADMIN_PATH, 'leadin_settings_section');
134
+
135
+ add_filter( 'update_option_leadin_options', array($this, 'update_option_leadin_options_callback'), 10, 2 );
136
+ }
137
+
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();
149
+ }
150
+
151
+ function print_hidden_settings_fields ()
152
+ {
153
+ // Hacky solution to solve the Settings API overwriting the default values
154
+ $options = get_option('leadin_options');
155
+ $li_installed = ( $options['li_installed'] ? $options['li_installed'] : 1 );
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"/>',
162
+ $li_installed
163
+ );
164
+
165
+ printf(
166
+ '<input id="li_db_version" type="hidden" name="leadin_options[li_db_version]" value="%s"/>',
167
+ $li_db_version
168
+ );
169
+
170
+ printf(
171
+ '<input id="ignore_settings_popup" type="hidden" name="leadin_options[ignore_settings_popup]" value="%d"/>',
172
+ $ignore_settings_popup
173
+ );
174
+
175
+ printf(
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 )
182
+ {
183
+ $user_email = $new_value["li_email"];
184
+ leadin_register_user();
185
+ }
186
+
187
+ /**
188
+ * Creates settings page
189
+ */
190
+ function leadin_plugin_options ()
191
+ {
192
+ global $wp_version;
193
+
194
+ leadin_track_plugin_activity("Loaded Settings Page");
195
+
196
+ if ( !current_user_can( 'manage_categories' ) )
197
+ {
198
+ wp_die(__('You do not have sufficient permissions to access this page.'));
199
+ }
200
+
201
+ // Update the settings popup flag when the settings page is visited for the first time
202
+ $li_options = get_option('leadin_options');
203
+
204
+ echo '<div id="leadin" class="li-settings wrap '. ( $wp_version < 3.8 && !is_plugin_active('mp6/mp6.php') ? 'pre-mp6' : ''). '">';
205
+
206
+ $this->leadin_header('LeadIn Settings');
207
+
208
+ if ( !$li_options['onboarding_complete'] && !isset($_GET['settings-updated']) )
209
+ $this->leadin_plugin_onboarding();
210
+ else
211
+ $this->leadin_plugin_settings();
212
+
213
+
214
+ $this->leadin_footer();
215
+
216
+ //end wrap
217
+ echo '</div>';
218
+
219
+ }
220
+
221
+ /**
222
+ * Creates onboarding settings page
223
+ */
224
+ function leadin_plugin_onboarding ()
225
+ {
226
+ leadin_track_plugin_activity("Loaded Onboarding Page");
227
+
228
+ ?>
229
+
230
+ <div class="steps">
231
+ <ol class="step-names">
232
+ <li class="step-name completed">Downloaded</li>
233
+ <li class="step-name completed">Activated</li>
234
+ <li class="step-name active">Confirm Email</li>
235
+ </ol>
236
+ <ul class="step-content">
237
+ <li class="step active">
238
+ <h2>Confirm your email</h2>
239
+ <form method="post" action="options.php">
240
+ <?php settings_fields('leadin_settings_options'); ?>
241
+ <p>
242
+ <?php $this->li_email_callback(); ?>
243
+ </p>
244
+
245
+ <input type="hidden" name="onboarding-complete" value="true">
246
+ <?php $this->print_hidden_settings_fields(); ?>
247
+
248
+ <input type="submit" name="submit" id="submit" class="button button-primary" value="<?php esc_attr_e('Save Settings'); ?>">
249
+ </form>
250
+ </li>
251
+ </ul>
252
+ </div>
253
+
254
+ <?php
255
+ }
256
+
257
+ /**
258
+ * Creates default settings page
259
+ */
260
+ function leadin_plugin_settings ()
261
+ {
262
+ ?>
263
+ <form method="post" action="options.php">
264
+ <?php
265
+ settings_fields('leadin_settings_options');
266
+ do_settings_sections(LEADIN_ADMIN_PATH);
267
+ submit_button('Save Settings');
268
+ ?>
269
+ </form>
270
+ <?php
271
+ }
272
+
273
+ /**
274
+ * Sanitize each setting field as needed
275
+ *
276
+ * @param array $input Contains all settings fields as array keys
277
+ */
278
+ public function sanitize ( $input )
279
+ {
280
+ $new_input = array();
281
+
282
+ if( isset( $input['li_email'] ) )
283
+ $new_input['li_email'] = sanitize_text_field( $input['li_email'] );
284
+
285
+ if( isset( $input['li_installed'] ) )
286
+ $new_input['li_installed'] = $input['li_installed'];
287
+
288
+ if( isset( $input['li_db_version'] ) )
289
+ $new_input['li_db_version'] = $input['li_db_version'];
290
+
291
+ if( isset( $input['onboarding_complete'] ) )
292
+ $new_input['onboarding_complete'] = $input['onboarding_complete'];
293
+
294
+ if( isset( $input['ignore_settings_popup'] ) )
295
+ $new_input['ignore_settings_popup'] = $input['ignore_settings_popup'];
296
+
297
+ return $new_input;
298
+ }
299
+
300
+ /**
301
+ * Prints email input for settings page
302
+ */
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
+ /**
315
+ * Creates power-up page
316
+ */
317
+ function leadin_power_ups_page ()
318
+ {
319
+ global $wp_version;
320
+
321
+ leadin_track_plugin_activity("Loaded Power-ups Page");
322
+
323
+ if ( !current_user_can( 'manage_categories' ) )
324
+ {
325
+ wp_die(__('You do not have sufficient permissions to access this page.'));
326
+ }
327
+
328
+ echo '<div id="leadin" class="li-settings wrap '. ( $wp_version < 3.8 && !is_plugin_active('mp6/mp6.php') ? 'pre-mp6' : ''). '">';
329
+
330
+ $this->leadin_header('LeadIn Power-ups');
331
+
332
+ ?>
333
+
334
+ <p>Get the most out of your LeadIn install with these powerful marketing powerups.</p>
335
+
336
+ <ul class="powerup-list">
337
+
338
+ <?php foreach ( $this->admin_power_ups as $power_up ) : ?>
339
+ <li class="powerup <?php echo ( $power_up->activated ? 'activated' : ''); ?>">
340
+ <h2><?php echo $power_up->power_up_name; ?></h2>
341
+ <img src="<?php echo LEADIN_PATH . '/images/' . $power_up->icon . '@2x.png'; ?>" height="80px" width="80px"/>
342
+ <p><?php echo $power_up->description; ?></p>
343
+ <p><a href="<?php echo $power_up->link_uri; ?>" target="_blank">Learn more</a></p>
344
+ <?php if ( $power_up->activated ) : ?>
345
+ <?php if ( ! $power_up->permanent ) : ?>
346
+ <a href="<?php echo get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_power_ups&leadin_action=deactivate&power_up=' . $power_up->slug; ?>" class="button button-secondary button-large">Deactivate</a>
347
+ <?php endif; ?>
348
+ <?php else : ?>
349
+ <a href="<?php echo get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_power_ups&leadin_action=activate&power_up=' . $power_up->slug; ?>" class="button button-primary button-large">Activate</a>
350
+ <?php endif; ?>
351
+
352
+ <?php if ( $power_up->activated || $power_up->permanent ) : ?>
353
+ <?php if ( $power_up->menu_link == 'contacts' ) : ?>
354
+ <a href="<?php echo get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_' . $power_up->menu_link; ?>" class="button button-secondary button-large">View Contacts</a>
355
+ <a href="<?php echo get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_settings'; ?>" class="button button-secondary button-large">Configure</a>
356
+ <?php else : ?>
357
+ <a href="<?php echo get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_' . $power_up->menu_link; ?>" class="button button-secondary button-large">Configure</a>
358
+ <?php endif; ?>
359
+ <?php endif; ?>
360
+ </li>
361
+ <?php endforeach; ?>
362
+
363
+ <li class="powerup">
364
+ <h2>Content Analytics</h2>
365
+ <img src="<?php echo LEADIN_PATH; ?>/images/powerup-icon-analytics@2x.png" height="80px" width="80px">
366
+ <p>See where all your conversions are coming from.</p>
367
+ <p><a href="http://leadin.com/content-analytics-plugin-wordpress/">Learn more</a></p>
368
+ <a disabled="true" class="button button-primary button-large">Coming soon</a>
369
+ </li>
370
+ <li class="powerup">
371
+ <h2>Your Idea</h2>
372
+ <img src="<?php echo LEADIN_PATH; ?>/images/powerup-icon-ideas@2x.png" height="80px" width="80px">
373
+ <p>Have an idea for a power-up? We'd love to hear it!</p>
374
+ <p>&nbsp;</p>
375
+ <a href="mailto:team@leadin.com" target="_blank" class="button button-primary button-large">Suggest an idea</a>
376
+ </li>
377
+ </ul>
378
+
379
+ <?php
380
+
381
+
382
+ $this->leadin_footer();
383
+
384
+ //end wrap
385
+ echo '</div>';
386
+
387
+ }
388
+
389
+ function check_admin_action ( )
390
+ {
391
+ if ( isset( $_GET['leadin_action'] ) )
392
+ {
393
+ switch ( $_GET['leadin_action'] )
394
+ {
395
+ case 'activate' :
396
+
397
+ $power_up = stripslashes( $_GET['power_up'] );
398
+
399
+ WPLeadIn::activate_power_up( $power_up, FALSE );
400
+ //ob_end_clean();
401
+ leadin_track_plugin_activity($power_up . " power-up activated");
402
+ wp_redirect(get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_power_ups');
403
+ exit;
404
+
405
+ break;
406
+
407
+ case 'deactivate' :
408
+
409
+ $power_up = stripslashes( $_GET['power_up'] );
410
+
411
+ WPLeadIn::deactivate_power_up( $power_up, FALSE );
412
+ leadin_track_plugin_activity($power_up . " power-up deactivated");
413
+ wp_redirect(get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_power_ups');
414
+ exit;
415
+
416
+ break;
417
+ }
418
+ }
419
+ }
420
+
421
+ //=============================================
422
+ // Admin Styles & Scripts
423
+ //=============================================
424
+
425
+ /**
426
+ * Adds admin style sheets
427
+ */
428
+ function add_leadin_admin_styles ()
429
+ {
430
+ wp_register_style('leadin-admin-css', LEADIN_PATH . '/admin/css/leadin-admin.css');
431
+ wp_enqueue_style('leadin-admin-css');
432
+ }
433
+
434
+ //=============================================
435
+ // Internal Class Functions
436
+ //=============================================
437
+
438
+ /**
439
+ * Creates postbox for admin
440
+ * @param string
441
+ * @param string
442
+ * @param string
443
+ * @param bool
444
+ * @return string HTML for postbox
445
+ */
446
+ function leadin_postbox ( $id, $title, $content, $handle = TRUE )
447
+ {
448
+ $postbox_wrap = "";
449
+ $postbox_wrap .= '<div id="' . $id . '" class="postbox leadin-admin-postbox">';
450
+ $postbox_wrap .= ( $handle ? '<div class="handlediv" title="Click to toggle"><br /></div>' : '' );
451
+ $postbox_wrap .= '<h3><span>' . $title . '</span></h3>';
452
+ $postbox_wrap .= '<div class="inside">' . $content . '</div>';
453
+ $postbox_wrap .= '</div>';
454
+ return $postbox_wrap;
455
+ }
456
+
457
+ /**
458
+ * Prints the admin page title, icon and help notification
459
+ * @param string
460
+ */
461
+ function leadin_header ( $page_title = '' )
462
+ {
463
+ ?>
464
+ <?php screen_icon('leadin'); ?>
465
+ <h2><?php echo $page_title; ?></h2>
466
+
467
+ <?php if ( isset($_GET['settings-updated']) ) : ?>
468
+ <div id="message" class="updated">
469
+ <p><strong><?php _e('Settings saved.') ?></strong></p>
470
+ </div>
471
+ <?php endif;
472
+ }
473
+
474
+ function leadin_footer ()
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 -->
483
+ <script type="text/javascript">
484
+ (function() {
485
+ var se = document.createElement('script'); se.type = 'text/javascript'; se.async = true;
486
+ se.src = '//commondatastorage.googleapis.com/code.snapengage.com/js/b7667cce-a26d-4440-a716-7c4b9f086705.js';
487
+ var done = false;
488
+ se.onload = se.onreadystatechange = function() {
489
+ if (!done&&(!this.readyState||this.readyState==='loaded'||this.readyState==='complete')) {
490
+ done = true;
491
+ // Place your SnapEngage JS API code below
492
+ // SnapEngage.allowChatSound(true); // Example JS API: Enable sounds for Visitors.
493
+ }
494
+ };
495
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(se, s);
496
+ })();
497
+ </script>
498
+ <!-- end SnapEngage code -->
499
+ <?php
500
+ }
501
+ }
502
+
503
+ ?>
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_appearance.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-clip.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-origin.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_background-size.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_border-radius.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-shadow.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box-sizing.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_box.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_columns.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_filter.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_font-face.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_hyphenation.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_images.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_inline-block.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_opacity.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_regions.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_shared.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_text-shadow.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transform.scssc ADDED
Binary file
admin/sass/.sass-cache/a2b7e568cca225148f3e4dc8c9c0bdfe1174b763/_transition.scssc ADDED
Binary file
admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_clearfix.scssc ADDED
Binary file
admin/sass/.sass-cache/aa1b4daeb1aa66522abf94710543639284144823/_hacks.scssc ADDED
Binary file
admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_css3.scssc ADDED
Binary file
admin/sass/.sass-cache/ba1a1c8b7407a74496e0f1bb0af5e060acd9c6b4/_support.scssc ADDED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contact_detail_page.sassc ADDED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_contacts_list_page.sassc ADDED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_settings_page.sassc ADDED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_tables.sassc ADDED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/_variables.sassc ADDED
Binary file
admin/sass/.sass-cache/f44f973eb6f81b0c357f9f23e88bc1c2233de3b4/leadin-admin.sassc ADDED
Binary file
admin/sass/_contact_detail_page.sass ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #leadin
2
+
3
+ .header-wrap
4
+ @include pie-clearfix
5
+ padding: 9px 15px 4px 0
6
+
7
+ h1, img
8
+ float: left
9
+
10
+ h1
11
+ padding: 0 0 0 10px
12
+ margin: 0
13
+
14
+ .contact-name
15
+ line-height: 40px
16
+
17
+ .contact-info
18
+
19
+ label
20
+ font-weight: bold
21
+ line-height: 1
22
+ cursor: default
23
+
24
+ .contact-history
25
+ padding-left: 20px
26
+ margin-left: 20px
27
+ border-left: 2px solid $color-border
28
+
29
+ .session
30
+
31
+ & + .session
32
+ margin-top: $base-vertical-unit*5
33
+
34
+ .session-date
35
+ position: relative
36
+
37
+ &:before
38
+ content: "\2022"
39
+ font-size: 32px
40
+ line-height: 0
41
+ height: 31px
42
+ width: 31px
43
+ position: absolute
44
+ left: -27px
45
+ top: 9px
46
+ color: $color-border
47
+
48
+ .events
49
+ background-color: #fff
50
+ border: 1px solid $color-border
51
+ @include box-shadow(0 1px 1px rgba(0,0,0,.04))
52
+
53
+ .event
54
+ margin: 0
55
+ padding: 10px 20px
56
+ border:
57
+ bottom: 1px solid $color-border
58
+ left: 4px solid
59
+
60
+ &:first-child
61
+ border-top: 0
62
+
63
+ &.pageview
64
+ border-left-color: $blue
65
+ color: $blue
66
+
67
+ &.form-submission
68
+ border-left-color: $orange
69
+ color: $orange
70
+
71
+ &.source
72
+ border-left-color: $green
73
+ color: $green
74
+
75
+ .event-title
76
+ margin: 0
77
+ font-size: 13px
78
+ font-weight: 600
79
+
80
+ .event-time-range
81
+ float: right
82
+ font-weight: 400
83
+ font-size: 0.85em
84
+ color: $color-text-light
85
+
86
+ .event-detail
87
+ margin-top: 20px
88
+ color: $color-text
89
+
90
+ li + li
91
+ padding-top: $base-vertical-unit
92
+ border-top: 1px solid $color-border-light
93
+
94
+ .visit-source
95
+ p
96
+ margin: 0
97
+ color: $blue-dark
98
+
99
+ .pageview-url
100
+ margin: 0
101
+ color: $color-text-lighter
102
+
103
+ .field-label
104
+ text-transform: uppercase
105
+ letter-spacing: 0.05em
106
+ color: $color-text-light
107
+ margin-bottom: 6px
108
+ font-size: 0.9em
109
+
110
+ .field-value
111
+ margin: 0
112
+
113
+ &.pre-mp6
114
+
115
+ .events
116
+ background-color: #f9f9f9
admin/sass/_contacts_list_page.sass ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #leadin-contacts
2
+
3
+ th
4
+
5
+ &#source
6
+ width: 20%
7
+
8
+ &#visits, &#submissions
9
+ width: 8%
10
+
11
+ &#status, &#last_visit, &#date, &#pageviews
12
+ width: 10%
13
+
admin/sass/_grid.sass ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "susy"
2
+
3
+ // Settings
4
+
5
+ $break: 782px 12
6
+
7
+ $total-columns: 12
8
+ $column-width: 80px
9
+ $gutter-width: 20px
10
+ $grid-padding: 20px
11
+
12
+ #leadin
13
+ @include container($total-columns, $break)
14
+ margin-left: 0
15
+ padding: 0
16
+
17
+ *
18
+ box-sizing: border-box
19
+
20
+ // Layout
21
+
22
+ @include at-breakpoint($break)
admin/sass/_powerups_page.sass ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .powerup-list
2
+ margin: 0
3
+
4
+ .powerup
5
+ border: 2px solid
6
+ width: 20%
7
+ min-width: 250px
8
+ float: left
9
+ margin: $grid-padding
10
+ padding: 15px
11
+ background-color: #f9f9f9
12
+ border-color: #ccc
13
+ text-align: center
14
+ @include border-radius(10px)
15
+
16
+ h2, p, img
17
+ margin: 0
18
+ padding: 0
19
+ color: #666
20
+ margin-bottom: 15px
21
+
22
+ &.activated
23
+ background-color: $teal-background
24
+ border-color: $teal-dark
25
+
26
+ h2, p
27
+ color: $teal-dark
admin/sass/_settings_page.sass ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #icon-leadin
2
+ background: url('../../images/leadin-icon-32x32.png') top center no-repeat
3
+
4
+ .help-notification
5
+ background: #d9edf7
6
+ border: 1px solid #bce8f1
7
+ padding: 10px
8
+ -webkit-border-radius: 3px
9
+ -moz-border-radius: 3px
10
+ border-radius: 3px
11
+
12
+ .toplevel_page_leadin_contacts .wp-menu-image img
13
+ width: 16px
14
+ height: 16px
15
+
16
+ .leadin-contact-avatar
17
+ margin-right: 10px
18
+
19
+ .steps
20
+ margin: $base-vertical-unit*8 auto 0
21
+ text-align: center
22
+ max-width: 600px
23
+
24
+ h3, p
25
+ margin: 0
26
+
27
+ .step-names
28
+ margin: 0
29
+ +pie-clearfix
30
+
31
+ .step-name
32
+ color: #ccc
33
+ display: list-item
34
+ float: left
35
+ width: 33%
36
+ margin: 0
37
+ padding-bottom: $base-vertical-unit*3
38
+ font-size: 16px
39
+ list-style: decimal inside none
40
+
41
+ &.active
42
+ color: $teal-dark
43
+ background-image: url(../../images/triangle.png)
44
+ background-position: bottom center
45
+ background-repeat: no-repeat
46
+
47
+ &.completed
48
+ list-style-image: url(../../images/checkmark.png)
49
+
50
+ .step-content
51
+ margin: 0
52
+
53
+ .description
54
+ margin: 10px 0 20px
55
+ display: block
56
+
57
+ .step
58
+ display: none
59
+ padding: $base-vertical-unit*3
60
+ background-color: $teal-background
61
+ border: 2px solid $teal
62
+ +border-radius(5px)
63
+ color: $teal-dark
64
+
65
+ h2
66
+ color: $teal-dark
67
+ margin:
68
+ top: 0
69
+ bottom: $base-vertical-unit*3
70
+
71
+ ol
72
+ text-align: left
73
+ margin-bottom: $base-vertical-unit*3
74
+
75
+ label
76
+ text-align: right
77
+
78
+ &.active
79
+ display: block
80
+
81
+ .form-table
82
+ th
83
+ display: none
84
+
85
+ td
86
+ text-align: center
87
+ width: auto
88
+ display: block
89
+
90
+ input
91
+ width: 100%
92
+ font-size: 16px
93
+ line-height: 1.5
94
+ padding: 7px 10px
95
+ display: block
admin/sass/_tables.sass ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ $table-controls-pading: $base-vertical-unit*2
2
+ $table-controls-border-size: 2px
3
+
4
+ #leadin
5
+
6
+ .top_table_controls
7
+ border-bottom: $table-controls-border-size solid $color-border
8
+ margin: 0 0 $base-vertical-unit*3 0
9
+ margin-bottom: $base-vertical-unit*3 - $table-controls-border-size
10
+ @include pie-clearfix
11
+
12
+ .table_segment_picker
13
+ float: left
14
+ margin: 0
15
+
16
+ li
17
+ display: inline-block
18
+ margin: 0
19
+
20
+ & + li
21
+ margin-left: 2em
22
+ a
23
+ display: block
24
+ padding: $table-controls-pading 0
25
+ line-height: $base-vertical-unit*4
26
+ font-weight: 300
27
+ font-size: 20px
28
+ text-decoration: none
29
+
30
+ &.current
31
+ margin-bottom: -2px
32
+ font-weight: 400
33
+ border-bottom: 2px solid $color-primary
34
+
35
+ &.current, &:hover, &:active
36
+ color: $color-primary
37
+
38
+ .table_search
39
+ float: right
40
+ padding: ($table-controls-pading - 2px) 0
41
+ padding-bottom: $table-controls-pading - 3px
42
+
43
+ table
44
+ .leadin-contact-avatar
45
+ float: left
46
+
47
+ &.pre-mp6
48
+ .table_search
49
+ float: right
50
+ padding: $table-controls-pading 0
51
+ padding-bottom: $table-controls-pading - 1px
52
+
53
+ table
54
+ background-color: #fff
55
+ border-color: $color-border
56
+
57
+ tr
58
+
59
+ &.alternate
60
+ background-color: #fff
61
+
62
+ th, td
63
+ border-top: 0
64
+ padding: $base-vertical-unit*2 $base-vertical-unit $base-vertical-unit*2 - 1px
65
+
66
+ a
67
+ padding: 0
68
+
69
+ th[scope="col"]
70
+ background: #eee
71
+ font-family: $font-sans
72
+ font-size: 12px
73
+ text-shadow: none
74
+
75
+ td
76
+ border-color: $color-border
77
+ line-height: $base-vertical-unit*3
78
+ font-size: 14px
79
+
80
+ .row-actions
81
+ float: left
admin/sass/_variables.sass ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ $base-vertical-unit: 6px
2
+ $font-sans: sans-serif
3
+
4
+ $color-primary: #F66000
5
+ $color-secondary: #F66000
6
+
7
+ $color-text: #444
8
+ $color-text-light: #999
9
+ $color-text-lighter: #ccc
10
+
11
+ $color-border: #dedede
12
+ $color-border-light: #eeeeee
13
+
14
+ // orange
15
+ $orange-darker: #71350E
16
+ $orange-dark: #B34A12
17
+ $orange: #F66000
18
+ $orange-light: #F88E4B
19
+ $orange-lighter: #FBBF99
20
+ $orange-background: #FDDFCC
21
+
22
+ // blue
23
+ $blue-darker: #1D5072
24
+ $blue-dark: #1F6696
25
+ $blue: #2288CC
26
+ $blue-light: #64AADA
27
+ $blue-lighter: #A7CFEB
28
+ $blue-background: #D3E7F5
29
+
30
+ // teal
31
+ $teal-darker: #1D524C
32
+ $teal-dark: #1F7D71
33
+ $teal: #22AA99
34
+ $teal-light: #64C2B6
35
+ $teal-lighter: #A7DDD6
36
+ $teal-background: #D3EEEB
37
+
38
+ // fucia
39
+ $fucia-darker: #5A2A45
40
+ $fucia-dark: #1F7E72
41
+ $fucia: #BB4488
42
+ $fucia-light: #CF7BAA
43
+ $fucia-lighter: #E4B4CF
44
+ $fucia-background: #F1DAE7
45
+
46
+ // green
47
+ $green-darker: #4C530F
48
+ $green-dark: #A27E18
49
+ $green: #99AA1F
50
+ $green-light: #B7C24B
51
+ $green-lighter: #D6DD99
52
+ $green-background: #EBEECC
53
+
54
+ // grey
55
+ $grey: #666
56
+ $grey-light: #999
57
+ $grey-lighter: #ccc
58
+ $grey-background: #f9f9f9
59
+ $grey-dark: #444
60
+ $grey-darker: #222
admin/sass/config.rb ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Compass is a great cross-platform tool for compiling SASS.
2
+ # This compass config file will allow you to
3
+ # quickly dive right in.
4
+ # For more info about compass + SASS: http://net.tutsplus.com/tutorials/html-css-techniques/using-compass-and-sass-for-css-in-your-next-project/
5
+
6
+ require 'susy'
7
+
8
+ #########
9
+ # 1. Set this to the root of your project when deployed:
10
+ http_path = "/"
11
+
12
+ # 2. probably don't need to touch these
13
+ css_dir = "../css"
14
+ sass_dir = "./"
15
+ images_dir = "../../images"
16
+ environment = :development
17
+ relative_assets = true
18
+
19
+
20
+ # 3. You can select your preferred output style here (can be overridden via the command line):
21
+ output_style = :expanded
22
+
23
+ # 4. When you are ready to launch your WP theme comment out (3) and uncomment the line below
24
+ # output_style = :compressed
25
+
26
+ # To disable debugging comments that display the original location of your selectors. Uncomment:
27
+ # line_comments = false
28
+
29
+ # don't touch this
30
+ preferred_syntax = :sass
admin/sass/leadin-admin.sass ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "compass/utilities/general/clearfix"
2
+ @import "compass/css3"
3
+
4
+ @import variables.sass
5
+ @import grid.sass
6
+ @import tables.sass
7
+
8
+ //WordPress stuff
9
+ #leadin
10
+ padding-right: 20px
11
+
12
+ @media screen and (min-width: 500px)
13
+ padding-right: 10px
14
+
15
+ label
16
+ cursor: default
17
+
18
+ .col-wrap
19
+ padding: 0 14px 0 0
20
+
21
+ .metabox-holder
22
+ +pie-clearfix
23
+
24
+ #leadin-footer
25
+ +pie-clearfix
26
+ clear: both
27
+ margin-top: $base-vertical-unit*8
28
+ color: $color-text-light
29
+ border-top: 1px solid $color-border
30
+
31
+ .support .sharing
32
+ height: 18px
33
+ text-align: left
34
+
35
+ @media screen and (min-width: 500px)
36
+ .support, .version, .sharing
37
+ width: 50%
38
+ float: left
39
+
40
+ .sharing
41
+ text-align: right
42
+
43
+ #wp-admin-bar-leadin-admin-menu
44
+
45
+ img
46
+ height: 16px
47
+ width: 16px
48
+ opacity: 0.6
49
+
50
+ // Pages
51
+ @import settings_page.sass
52
+ @import contacts_list_page.sass
53
+ @import contact_detail_page.sass
54
+ @import powerups_page.sass
55
+
frontend/js/jquery.cookie.js ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*!
2
+ * jQuery Cookie Plugin v1.4.0
3
+ * https://github.com/carhartl/jquery-cookie
4
+ *
5
+ * Copyright 2013 Klaus Hartl
6
+ * Released under the MIT license
7
+ */
8
+ (function (factory) {
9
+ if (typeof define === 'function' && define.amd) {
10
+ // AMD. Register as anonymous module.
11
+ define(['jquery'], factory);
12
+ } else {
13
+ // Browser globals.
14
+ factory(jQuery);
15
+ }
16
+ }(function ($) {
17
+
18
+ var pluses = /\+/g;
19
+
20
+ function encode(s) {
21
+ return config.raw ? s : encodeURIComponent(s);
22
+ }
23
+
24
+ function decode(s) {
25
+ return config.raw ? s : decodeURIComponent(s);
26
+ }
27
+
28
+ function stringifyCookieValue(value) {
29
+ return encode(config.json ? JSON.stringify(value) : String(value));
30
+ }
31
+
32
+ function parseCookieValue(s) {
33
+ if (s.indexOf('"') === 0) {
34
+ // This is a quoted cookie as according to RFC2068, unescape...
35
+ s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
36
+ }
37
+
38
+ try {
39
+ // Replace server-side written pluses with spaces.
40
+ // If we can't decode the cookie, ignore it, it's unusable.
41
+ // If we can't parse the cookie, ignore it, it's unusable.
42
+ s = decodeURIComponent(s.replace(pluses, ' '));
43
+ return config.json ? JSON.parse(s) : s;
44
+ } catch(e) {}
45
+ }
46
+
47
+ function read(s, converter) {
48
+ var value = config.raw ? s : parseCookieValue(s);
49
+ return $.isFunction(converter) ? converter(value) : value;
50
+ }
51
+
52
+ var config = $.cookie = function (key, value, options) {
53
+
54
+ // Write
55
+ if (value !== undefined && !$.isFunction(value)) {
56
+ options = $.extend({}, config.defaults, options);
57
+
58
+ if (typeof options.expires === 'number') {
59
+ var days = options.expires, t = options.expires = new Date();
60
+ t.setDate(t.getDate() + days);
61
+ }
62
+
63
+ return (document.cookie = [
64
+ encode(key), '=', stringifyCookieValue(value),
65
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
66
+ options.path ? '; path=' + options.path : '',
67
+ options.domain ? '; domain=' + options.domain : '',
68
+ options.secure ? '; secure' : ''
69
+ ].join(''));
70
+ }
71
+
72
+ // Read
73
+
74
+ var result = key ? undefined : {};
75
+
76
+ // To prevent the for loop in the first place assign an empty array
77
+ // in case there are no cookies at all. Also prevents odd result when
78
+ // calling $.cookie().
79
+ var cookies = document.cookie ? document.cookie.split('; ') : [];
80
+
81
+ for (var i = 0, l = cookies.length; i < l; i++) {
82
+ var parts = cookies[i].split('=');
83
+ var name = decode(parts.shift());
84
+ var cookie = parts.join('=');
85
+
86
+ if (key && key === name) {
87
+ // If second argument (value) is a function it's a converter...
88
+ result = read(cookie, value);
89
+ break;
90
+ }
91
+
92
+ // Prevent storing a cookie that we couldn't decode.
93
+ if (!key && (cookie = read(cookie)) !== undefined) {
94
+ result[name] = cookie;
95
+ }
96
+ }
97
+
98
+ return result;
99
+ };
100
+
101
+ config.defaults = {};
102
+
103
+ $.removeCookie = function (key, options) {
104
+ if ($.cookie(key) === undefined) {
105
+ return false;
106
+ }
107
+
108
+ // Must not alter options, thus extending a fresh object...
109
+ $.cookie(key, '', $.extend({}, options, { expires: -1 }));
110
+ return !$.cookie(key);
111
+ };
112
+
113
+ }));
frontend/js/leadin.js ADDED
@@ -0,0 +1,435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var page_title = jQuery(document).find("title").text();
2
+ var page_url = window.location.href;
3
+ var page_referrer = document.referrer;
4
+ var form_saved = false;
5
+
6
+ jQuery(document).ready( function ( $ ) {
7
+
8
+ var hashkey = $.cookie("li_hash");
9
+ var li_submission_cookie = $.cookie("li_submission");
10
+
11
+ // The submission didn't officially finish before the page refresh, so try it again
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 )
22
+ {
23
+ hashkey = Math.random().toString(36).slice(2);
24
+ $.cookie("li_hash", hashkey, {path: "/", domain: ""});
25
+ leadin_insert_lead(hashkey, page_referrer);
26
+ }
27
+
28
+ leadin_log_pageview(hashkey, page_title, page_url, page_referrer, $.cookie('li_last_visit'));
29
+
30
+ var date = new Date();
31
+ var current_time = date.getTime();
32
+ date.setTime(date.getTime() + (60 * 60 * 1000));
33
+
34
+ // The li_last_visit has expired, so check to see if this is a stale contact that has been merged
35
+ if ( !$.cookie('li_last_visit') )
36
+ {
37
+ leadin_check_merged_contact(hashkey);
38
+ }
39
+
40
+ $.cookie("li_last_visit", current_time, {path: "/", domain: "", expires: date});
41
+ });
42
+
43
+ jQuery(function($){
44
+
45
+ // Many WordPress sites run outdated version of jQuery. This is a fix to support jQuery < 1.7.0 and futureproof the plugin when bind, live, etc are deprecated
46
+ if ( $.versioncompare($.fn.jquery, '1.7.0') != -1 )
47
+ {
48
+ $(document).on('submit', 'form', function( e ) {
49
+ var $form = $(this).closest('form');
50
+ leadin_submit_form($form, $);
51
+ });
52
+ }
53
+ else
54
+ {
55
+ $(document).bind('submit', 'form', function( e ) {
56
+ var $form = $(this).closest('form');
57
+ leadin_submit_form($form, $);
58
+ });
59
+ }
60
+ });
61
+
62
+ 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
71
+ $this.find('input[type!="submit"], textarea').not('input[type="hidden"], input[type="radio"], input[type="password"]').each( function ( index ) {
72
+ var $element = $(this);
73
+ var $value = $element.val();
74
+
75
+ if ( !$element.is(':visible' ) )
76
+ return true;
77
+
78
+ // Check if input has an attached lable using for= tag
79
+ var $label = $("label[for='" + $element.attr('id') + "']").text();
80
+
81
+ // Check for label in same container immediately before input
82
+ if ($label.length == 0)
83
+ {
84
+ $label = $element.prev('label').not('.li_used').addClass('li_used').first().text();
85
+
86
+ if ( !$label.length )
87
+ {
88
+ $label = $element.prevAll('b, strong, span').text(); // Find previous closest string
89
+ }
90
+ }
91
+
92
+ // Check for label in same container immediately after input
93
+ if ($label.length == 0)
94
+ {
95
+ $label = $element.next('label').not('.li_used').addClass('li_used').first().text();
96
+
97
+ if ( !$label.length )
98
+ {
99
+ $label = $element.nextAll('b, strong, span').text(); // Find next closest string
100
+ }
101
+ }
102
+
103
+ // Checks the parent for a label or bold text
104
+ if ($label.length == 0)
105
+ {
106
+ $label = $element.parent().find('label, b, strong').not('.li_used').first().text();
107
+ }
108
+
109
+ // Checks the parent's parent for a label or bold text
110
+ if ($label.length == 0)
111
+ {
112
+ if ( $.contains($this, $element.parent().parent()) )
113
+ {
114
+ $label = $element.parent().parent().find('label, b, strong').first().text();
115
+ }
116
+ }
117
+
118
+ // Looks for closests p tag parent, and looks for label inside
119
+ if ( $label.length == 0 )
120
+ {
121
+ $p = $element.closest('p').not('.li_used').addClass('li_used');
122
+
123
+ // This gets the text from the p tag parent if it exists
124
+ if ( $p.length )
125
+ {
126
+ $label = $p.text();
127
+ $label = $.trim($label.replace($value, "")); // Hack to exclude the textarea text from the label text
128
+ }
129
+ }
130
+
131
+ // Check for placeholder attribute
132
+ if ( $label.length == 0 )
133
+ {
134
+ if ( $element.attr('placeholder') !== undefined )
135
+ {
136
+ $label = $element.attr('placeholder').toString();
137
+ }
138
+ }
139
+
140
+ if ( $label.length == 0 )
141
+ {
142
+ if ( $element.attr('name') !== undefined )
143
+ {
144
+ $label = $element.attr('name').toString();
145
+ }
146
+ }
147
+
148
+ if ( $element.is(':checkbox') )
149
+ {
150
+ if ( $element.is(':checked'))
151
+ {
152
+ $value = 'Checked';
153
+ }
154
+ else
155
+ {
156
+ $value = 'Not checked';
157
+ }
158
+ }
159
+
160
+ var $label_text = $.trim($label.replaceArray(["(", ")", "required", "Required", "*", ":"], [""]));
161
+
162
+ push_form_field($label_text, $value, form_fields);
163
+
164
+ if ( $value.indexOf('@') != -1 && $value.indexOf('.') != -1 && !lead_email )
165
+ lead_email = $value;
166
+ });
167
+
168
+ var radio_groups = [];
169
+ var rbg_label_values = [];
170
+ $this.find(":radio").each(function(){
171
+ if ( $.inArray(this.name, radio_groups) == -1 )
172
+ radio_groups.push(this.name);
173
+ rbg_label_values.push($(this).val());
174
+ });
175
+
176
+ for ( var i = 0; i < radio_groups.length; i++ )
177
+ {
178
+ var $rbg = $("input:radio[name='" + radio_groups[i] + "']");
179
+ var $rbg_value = $("input:radio[name='" + radio_groups[i] + "']:checked").val();
180
+
181
+ if ( $this.find('.gfield').length ) // Hack for gravity forms
182
+ $p = $rbg.closest('.gfield').not('.li_used').addClass('li_used');
183
+ else if ( $this.find('.frm_form_field').length ) // Hack for Formidable
184
+ $p = $rbg.closest('.frm_form_field').not('.li_used').addClass('li_used');
185
+ else
186
+ $p = $rbg.closest('div, p').not('.li_used').addClass('li_used');
187
+
188
+ // This gets the text from the p tag parent if it exists
189
+ if ( $p.length )
190
+ {
191
+ //$p.find('label, strong, span, b').html();
192
+ $rbg_label = $p.text();
193
+ $rbg_label = $.trim($rbg_label.replaceArray(rbg_label_values, [""]).replace($p.find('.gfield_description').text(), ''));
194
+ // Remove .gfield_description from gravity forms
195
+ }
196
+
197
+ var rgb_selected = ( !$("input:radio[name='" + radio_groups[i] + "']:checked").val() ) ? 'not selected' : $("input:radio[name='" + radio_groups[i] + "']:checked").val();
198
+
199
+ push_form_field($rbg_label, rgb_selected, form_fields);
200
+ }
201
+
202
+ $this.find('select').each( function ( ) {
203
+ var $select = $(this);
204
+ var $select_label = $("label[for='" + $select.attr('id') + "']").text();
205
+
206
+ if ( !$select_label.length )
207
+ {
208
+ var select_values = [];
209
+ $select.find("option").each(function(){
210
+ if ( $.inArray($(this).val(), select_values) == -1 )
211
+ select_values.push($(this).val());
212
+ });
213
+
214
+ $p = $select.closest('div, p').not('.li_used').addClass('li_used');
215
+
216
+ if ( $this.find('.gfield').length ) // Hack for gravity forms
217
+ $p = $select.closest('.gfield').not('.li_used').addClass('li_used');
218
+ else
219
+ {
220
+ $p = $select.closest('div, p').addClass('li_used');
221
+ }
222
+
223
+ if ( $p.length )
224
+ {
225
+ $select_label = $p.text();
226
+ $select_label = $.trim($select_label.replaceArray(select_values, [""]).replace($p.find('.gfield_description').text(), ''));
227
+ }
228
+ }
229
+
230
+ push_form_field($select_label, $select.val(), form_fields);
231
+ });
232
+
233
+ $this.find('.li_used').removeClass('li_used'); // Clean up added classes
234
+
235
+ if ( $this.find('#comment_post_ID').length )
236
+ {
237
+ form_submission_type = 'comment';
238
+ }
239
+
240
+ // Save submission into database, send LeadIn email, and submit form as usual
241
+ if ( lead_email )
242
+ {
243
+ var submission_hash = Math.random().toString(36).slice(2);
244
+ var hashkey = $.cookie("li_hash");
245
+ var json_form_fields = JSON.stringify(form_fields);
246
+
247
+ var form_submission = {};
248
+ form_submission = {
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,
255
+ "form_submission_type": form_submission_type,
256
+ };
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
+ {
267
+ form_saved = true;
268
+ }
269
+ }
270
+
271
+ function leadin_check_merged_contact ( hashkey )
272
+ {
273
+ jQuery.ajax({
274
+ type: 'POST',
275
+ url: li_ajax.ajax_url,
276
+ data: {
277
+ "action": "leadin_check_merged_contact",
278
+ "li_id": hashkey
279
+ },
280
+ success: function(data){
281
+ // Force override the current tracking with the merged value
282
+ var json_data = jQuery.parseJSON(data);
283
+ if ( json_data )
284
+ jQuery.cookie("li_hash", json_data, {path: "/", domain: ""});
285
+ },
286
+ error: function ( error_data ) {
287
+ //alert(error_data);
288
+ }
289
+ });
290
+ }
291
+
292
+ function leadin_check_visitor_status ( hashkey, callback )
293
+ {
294
+ jQuery.ajax({
295
+ type: 'POST',
296
+ url: li_ajax.ajax_url,
297
+ data: {
298
+ "action": "leadin_check_visitor_status",
299
+ "li_id": hashkey
300
+ },
301
+ success: function(data){
302
+ // Force override the current tracking with the merged value
303
+ var json_data = jQuery.parseJSON(data);
304
+
305
+ if ( callback )
306
+ callback(json_data);
307
+ },
308
+ error: function ( error_data ) {
309
+ //alert(error_data);
310
+ }
311
+ });
312
+ }
313
+
314
+ function leadin_log_pageview ( hashkey, page_title, page_url, page_referrer, last_visit )
315
+ {
316
+ jQuery.ajax({
317
+ type: 'POST',
318
+ url: li_ajax.ajax_url,
319
+ data: {
320
+ "action": "leadin_log_pageview",
321
+ "li_id": hashkey,
322
+ "li_title": page_title,
323
+ "li_url": page_url,
324
+ "li_referrer": page_referrer,
325
+ "li_last_visit": last_visit
326
+ },
327
+ success: function(data){
328
+ },
329
+ error: function ( error_data ) {
330
+ //alert(error_data);
331
+ }
332
+ });
333
+ }
334
+
335
+ function leadin_insert_lead ( hashkey, page_referrer ) {
336
+ jQuery.ajax({
337
+ type: 'POST',
338
+ url: li_ajax.ajax_url,
339
+ data: {
340
+ "action": "leadin_insert_lead",
341
+ "li_id": hashkey,
342
+ "li_referrer": page_referrer
343
+ },
344
+ success: function(data){
345
+ },
346
+ error: function ( error_data ) {
347
+ //alert(error_data);
348
+ }
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',
356
+ url: li_ajax.ajax_url,
357
+ data: {
358
+ "action": "leadin_insert_form_submission",
359
+ "li_submission_id": submission_haskey,
360
+ "li_id": hashkey,
361
+ "li_title": 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){
368
+ if ( Callback )
369
+ Callback(data);
370
+ },
371
+ error: function ( error_data ) {
372
+ //alert(error_data);
373
+ }
374
+ });
375
+
376
+ }
377
+
378
+ function push_form_field ( label, value, form_fields )
379
+ {
380
+ var field = {
381
+ label: label,
382
+ value: value
383
+ };
384
+
385
+ form_fields.push(field);
386
+ }
387
+
388
+ String.prototype.replaceArray = function(find, replace) {
389
+ var replaceString = this;
390
+ for (var i = 0; i < find.length; i++) {
391
+ if ( replace.length != 1 )
392
+ replaceString = replaceString.replace(find[i], replace[i]);
393
+ else
394
+ replaceString = replaceString.replace(find[i], replace[0]);
395
+ }
396
+ return replaceString;
397
+ };
398
+
399
+ /**
400
+ * Checks the version number of jQuery and compares to string
401
+ *
402
+ * @param string
403
+ * @param string
404
+ *
405
+ * @return bool
406
+ */
407
+
408
+ (function($){
409
+ $.versioncompare = function(version1, version2){
410
+ if ('undefined' === typeof version1) {
411
+ throw new Error("$.versioncompare needs at least one parameter.");
412
+ }
413
+ version2 = version2 || $.fn.jquery;
414
+ if (version1 == version2) {
415
+ return 0;
416
+ }
417
+ var v1 = normalize(version1);
418
+ var v2 = normalize(version2);
419
+ var len = Math.max(v1.length, v2.length);
420
+ for (var i = 0; i < len; i++) {
421
+ v1[i] = v1[i] || 0;
422
+ v2[i] = v2[i] || 0;
423
+ if (v1[i] == v2[i]) {
424
+ continue;
425
+ }
426
+ return v1[i] > v2[i] ? 1 : -1;
427
+ }
428
+ return 0;
429
+ };
430
+ function normalize(version){
431
+ return $.map(version.split('.'), function(value){
432
+ return parseInt(value, 10);
433
+ });
434
+ }
435
+ }(jQuery));
images/checkmark.png ADDED
Binary file
images/leadin-icon-32x32.png ADDED
Binary file
images/leadin-svg-icon.svg ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg width="32px" height="20px" viewBox="0 0 32 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
3
+ <title>leadin-svg-icon</title>
4
+ <description>Created with Sketch (http://www.bohemiancoding.com/sketch)</description>
5
+ <defs></defs>
6
+ <g id="Logo" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
7
+ <path d="M24.2659022,1.36625531 L18.6030712,19.9939886 L24.3824248,19.9939886 C25.180195,19.9939886 26.0085196,19.3966068 26.2321138,18.6610995 L31.4897464,1.36625531 C31.7135312,0.630121017 31.2475193,0.0333662628 30.450454,0.0333662628 L26.1155911,0.0333662628 C25.3178209,0.0333662628 24.4894964,0.63074804 24.2659022,1.36625531 Z" id="Rectangle-100" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
8
+ <path d="M13.4017203,6.68750917 L9.35655059,19.9939886 L15.1359041,19.9939886 C15.9336743,19.9939886 16.761737,19.3974683 16.9851132,18.6626783 L21.0302829,5.35619888 L15.2509294,5.35619888 C14.4531592,5.35619888 13.6250965,5.95271916 13.4017203,6.68750917 Z" id="Rectangle-100-copy" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
9
+ <path d="M2.53756131,12.008688 L0.514245522,18.664332 C0.291003421,19.3986811 0.757455297,19.9939886 1.55452066,19.9939886 L5.88938348,19.9939886 C6.68715368,19.9939886 7.51532043,19.3971259 7.73808978,18.664332 L10.1656212,10.6790315 L4.38626761,10.6790315 C3.58849742,10.6790315 2.76033066,11.2758941 2.53756131,12.008688 Z" id="Rectangle-100-copy-2" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
10
+ </g>
11
+ </svg>
images/powerup-icon-analytics.png ADDED
Binary file
images/powerup-icon-analytics@2x.png ADDED
Binary file
images/powerup-icon-ideas.png ADDED
Binary file
images/powerup-icon-ideas@2x.png ADDED
Binary file
images/powerup-icon-leads.png ADDED
Binary file
images/powerup-icon-leads@2x.png ADDED
Binary file
images/powerup-icon-subscribe.png ADDED
Binary file
images/powerup-icon-subscribe@2x.png ADDED
Binary file
images/powerups.png ADDED
Binary file
images/triangle.png ADDED
Binary file
inc/class-emailer.php ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ //=============================================
3
+ // LI_Emailer Class
4
+ //=============================================
5
+ class LI_Emailer {
6
+
7
+ /**
8
+ * Class constructor
9
+ */
10
+ function LI_Emailer ()
11
+ {
12
+
13
+ }
14
+
15
+ /**
16
+ * Sends the leads history email
17
+ *
18
+ * @param string
19
+ * @return bool $email_sent Whether the email contents were sent successfully. A true return value does not automatically mean that the user received the email successfully. It just only means that the method used was able to process the request without any errors.
20
+ */
21
+ function send_new_lead_email ( $hashkey )
22
+ {
23
+ $history = $this->get_lead_history($hashkey);
24
+
25
+ $avatar_img = "https://app.getsignals.com/avatar/image/?emails=" . $history->lead->lead_email;
26
+
27
+ // @EMAIL - Use this variable to concatenate your HTML
28
+ $body = "";
29
+
30
+ // Email Base open
31
+ $body .= "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'><html xmlns='http://www.w3.org/1999/xhtml' xmlns='http://www.w3.org/1999/xhtml'><head><meta http-equiv='Content-Type' content='text/html;charset=utf-8'/><meta name='viewport' content='width=device-width'/></head><body style='width: 100% !important;-webkit-text-size-adjust: 100%;-ms-text-size-adjust: 100%;color: #222222;display: block;font-family: Helvetica, Arial, sans-serif;font-weight: normal;text-align: left;line-height: 19px;font-size: 14px;margin: 0;padding: 0;'><style type='text/css'>a:hover{color: #2795b6 !important;}a:active{color: #2795b6 !important;}a:visited{color: #2ba6cb !important;}h1 a:active{color: #2ba6cb !important;}h2 a:active{color: #2ba6cb !important;}h3 a:active{color: #2ba6cb !important;}h4 a:active{color: #2ba6cb !important;}h5 a:active{color: #2ba6cb !important;}h6 a:active{color: #2ba6cb !important;}h1 a:visited{color: #2ba6cb !important;}h2 a:visited{color: #2ba6cb !important;}h3 a:visited{color: #2ba6cb !important;}h4 a:visited{color: #2ba6cb !important;}h5 a:visited{color: #2ba6cb !important;}h6 a:visited{color: #2ba6cb !important;}.button:hover table td{background: #2795b6 !important;}.tiny-button:hover table td{background: #2795b6 !important;}.small-button:hover table td{background: #2795b6 !important;}.medium-button:hover table td{background: #2795b6 !important;}.large-button:hover table td{background: #2795b6 !important;}.button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.tiny-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.tiny-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.tiny-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.small-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.small-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.small-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.medium-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.medium-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.medium-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.large-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.large-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.large-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.secondary:hover table td{background: #d0d0d0 !important;}.success:hover table td{background: #457a1a !important;}.alert:hover table td{background: #970b0e !important;}@media only screen and (max-width: 600px){table[class='body'] img{width: auto !important;height: auto !important;}table[class='body'] .container{width: 95% !important;}table[class='body'] .row{width: 100% !important;display: block !important;}table[class='body'] .wrapper{display: block !important;padding-right: 0 !important;}table[class='body'] .columns{table-layout: fixed !important;float: none !important;width: 100% !important;padding-right: 0px !important;padding-left: 0px !important;display: block !important;}table[class='body'] .column{table-layout: fixed !important;float: none !important;width: 100% !important;padding-right: 0px !important;padding-left: 0px !important;display: block !important;}table[class='body'] .wrapper.first .columns{display: table !important;}table[class='body'] .wrapper.first .column{display: table !important;}table[class='body'] table.columns td{width: 100%;}table[class='body'] table.column td{width: 100%;}table[class='body'] td.offset-by-one{padding-left: 0 !important;}table[class='body'] td.offset-by-two{padding-left: 0 !important;}table[class='body'] td.offset-by-three{padding-left: 0 !important;}table[class='body'] td.offset-by-four{padding-left: 0 !important;}table[class='body'] td.offset-by-five{padding-left: 0 !important;}table[class='body'] td.offset-by-six{padding-left: 0 !important;}table[class='body'] td.offset-by-seven{padding-left: 0 !important;}table[class='body'] td.offset-by-eight{padding-left: 0 !important;}table[class='body'] td.offset-by-nine{padding-left: 0 !important;}table[class='body'] td.offset-by-ten{padding-left: 0 !important;}table[class='body'] td.offset-by-eleven{padding-left: 0 !important;}table[class='body'] .expander{width: 9999px !important;}table[class='body'] .hide-for-small{display: none !important;}table[class='body'] .show-for-desktop{display: none !important;}table[class='body'] .show-for-small{display: inherit !important;}table[class='body'] .hide-for-desktop{display: inherit !important;}table[class='body'] .container.main{width: 100% !important;}}</style><table class='body' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;height: 100%;width: 100%;padding: 0;'><tr align='left' style='vertical-align: top; text-align: left; padding: 0;'><td class='center' align='center' valign='top' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;padding: 0 0 20px;'><center style='width: 100%;'>";
32
+
33
+ // Email Header open
34
+ $body .= "<table class='row header' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;padding: 0px;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='center' align='center' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;padding: 0;' valign='top'><center style='width: 100%;'><table class='container' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: inherit;width: 580px;margin:0 auto 10px auto; padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 10px 0px 0px;' align='left' valign='top'><table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='two sub-columns' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;width: 16.66667% !important;padding: 0px 3.44828% 10px 0px;' align='left' valign='top'><img width='77' height='77' src='" . $avatar_img . "' style='outline: none;text-decoration: none;-ms-interpolation-mode: bicubic;width: auto;max-width: 100%;float: left;clear: both;display: block;' align='left'/></td><td class='ten sub-columns last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: middle;text-align: left;width: 83.33333% !important;padding: 0px 0px 10px;' align='left' valign='middle'>";
35
+
36
+ $body .= "<h1 class='lead-name' style='color: #222222; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-align: left; line-height: 1.3; word-break: normal; font-size: 20px; margin: 0; padding: 0;' align='left'>" . $history->lead->lead_email . "</h1>";
37
+
38
+ // Email Header close
39
+ $body .= "</td><td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;padding: 0;' align='left' valign='top'></td></tr></table></td></tr></table></center></td></tr></table>";
40
+
41
+ // Main container open
42
+ $body .= "<table class='container main' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: inherit;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;padding: 0;' align='left' valign='top'>";
43
+
44
+ // Form Submission section open
45
+ $body .= "<table class='row section form-submission' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;display: block;background: #deedf8;padding: 0px;' bgcolor='#deedf8'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 0 0px 0 0;' align='left' valign='top'>";
46
+
47
+ // Form Submission section header
48
+ $body .= $this->build_submission_header($history->submission, FALSE);
49
+
50
+ // Form Submission Rows
51
+ $fields = json_decode(stripslashes($history->submission->form_fields), true);
52
+
53
+ foreach ( $fields as $field )
54
+ {
55
+ $body .= $this->build_submission_row($field);
56
+ }
57
+
58
+ // Form Submission Section Close
59
+ $body .= "</td></tr></table>";
60
+
61
+ // @EMAIL - end form section
62
+
63
+ // History section title
64
+ $body .= "<h2 class='section-title' style='color: #222222; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-align: left; line-height: 1.3; word-break: normal; font-size: 18px; margin: 0; padding: 30px 0px 0px 20px;' align='left'>Lead History</h2>";
65
+
66
+ foreach ( array_reverse($history->pageviews_by_session) as $session )
67
+ {
68
+ // History section open
69
+ $body .= "<table class='row section lead-history' style='border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 100%; position: relative; display: block; background: #ffefe6; padding: 0px; margin-top: 20px;' bgcolor='#ffefe6'><tr style='vertical-align: top; text-align: left; padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; position: relative; padding: 0 0px 0 0;' align='left' valign='top'>";
70
+
71
+ $body .= $this->build_history_header($session);
72
+
73
+ // History section rows
74
+ foreach ( $session as $pageview )
75
+ {
76
+ $body .= $this->build_history_row($pageview);
77
+ }
78
+
79
+ // History section close
80
+ $body .= '</td></tr></table>';
81
+
82
+ // @EMAIL - end session
83
+ }
84
+ // @EMAIL - end visit history
85
+
86
+ // Button row
87
+ $body .= $this->build_button_row($history->lead->lead_id);
88
+
89
+ // Main container close
90
+ $body .= '</td></tr></table>';
91
+
92
+ // Email Base close
93
+ $body .= '</center></td></tr></table></body></html>';
94
+
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
+
104
+ // Get email from plugin settings, if none set, use admin email
105
+ $options = get_option('leadin_options');
106
+ $to = ( $options['li_email'] ? $options['li_email'] : get_bloginfo('admin_email') ); // Get email from plugin settings, if none set, use admin email
107
+
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
+ $first_name = leadin_get_value_by_key('First name', $fields);
117
+ $subject = $first_name . " loved " . $history->submission->form_page_title . " and subscribed to your mailing list";
118
+ leadin_track_plugin_activity("New subscriber");
119
+ $this->send_subscriber_confirmation_email($history);
120
+ }
121
+ else
122
+ {
123
+ $subject = "New lead from " . get_bloginfo('name');
124
+ leadin_track_plugin_activity("New lead");
125
+ $subject .= " - Say hello to " . $history->lead->lead_email;
126
+ }
127
+
128
+ $email_sent = wp_mail($to, $subject, $body, $headers);
129
+ return $email_sent;
130
+ }
131
+
132
+ /**
133
+ * Gets lead history from the database (pageviews, form submission, and lead details)
134
+ *
135
+ * @param string
136
+ * @return object $history (pageviews_by_session, submission, lead)
137
+ */
138
+ function get_lead_history ( $hashkey )
139
+ {
140
+ global $wpdb;
141
+
142
+ $q = $wpdb->prepare(
143
+ "SELECT
144
+ pageview_id,
145
+ DATE_FORMAT(pageview_date, %s) AS pageview_day,
146
+ DATE_FORMAT(pageview_date, %s) AS pageview_date,
147
+ lead_hashkey, pageview_title, pageview_url, pageview_source, pageview_session_start
148
+ FROM
149
+ li_pageviews
150
+ WHERE
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);
154
+
155
+ $pageviews_by_session = array();
156
+ $cur_array = '0';
157
+ $count = 0;
158
+ foreach ( $pageviews as $pageview )
159
+ {
160
+ if ( $pageview->pageview_session_start )
161
+ {
162
+ $cur_array = $count;
163
+ $pageviews_by_session['session_' . $cur_array] = array();
164
+ }
165
+
166
+ $pageviews_by_session['session_' . $cur_array][] = $pageview;
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' ORDER BY form_datetime DESC LIMIT 1", '%b %e %l:%i%p', $hashkey);
171
+ $submission = $wpdb->get_row($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", $hashkey);
174
+ $lead = $wpdb->get_row($q);
175
+
176
+ $history = (object)NULL;
177
+ $history->pageviews_by_session = $pageviews_by_session;
178
+ $history->submission = $submission;
179
+ $history->lead = $lead;
180
+
181
+ return $history;
182
+ }
183
+
184
+ /**
185
+ * Builds the blue form submission header for the lead email
186
+ * @param object $submission
187
+ * @return string
188
+ */
189
+ function build_submission_header ( $submission, $confirmation_email = FALSE )
190
+ {
191
+ // @EMAIL Use these variables to construct the heading for the form section
192
+ $form_page_title = "<a href='" . $submission->form_page_url . "'>" . $submission->form_page_title . "</a>";
193
+ $form_submission_day = date('M j' , strtotime($submission->form_date));
194
+ $form_submission_time = date('g:i a', strtotime($submission->form_date));
195
+ $form_submission_type = $submission->form_type;
196
+
197
+ $submissionHeader = "";
198
+
199
+ // Form Submission header open
200
+ $submissionHeader .= "<table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='section-header' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;border-bottom-width: 1px;border-bottom-color: #c9e1f3;border-bottom-style: solid;background: #c9e1f3;padding: 15px 20px;' align='left' bgcolor='#c9e1f3' valign='top'>";
201
+
202
+ // Form Submission header content
203
+ $submissionHeader .= "<h3 style='color: #153d60;display: block;font-family: Helvetica, Arial, sans-serif;font-weight: bold;text-align: left;line-height: 1.3;word-break: normal;font-size: 16px;margin: 0;padding: 0;' align='left'>";
204
+
205
+ if ( $form_submission_type == "comment" )
206
+ {
207
+ $submissionHeader .= "Commented on " . $form_page_title . " on " . $form_submission_day . ' at ' . $form_submission_time;
208
+ }
209
+ else
210
+ {
211
+ $submissionHeader .= ( $confirmation_email ? "You filled out the subscription form on " : "Filled out a form on " ) . $form_page_title . " on " . $form_submission_day . ' at ' . $form_submission_time;
212
+ }
213
+
214
+ $submissionHeader .= "</h3>";
215
+
216
+ // Form Submission header close
217
+ $submissionHeader .= "</td><td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;border-bottom-color: #c9e1f3;border-bottom-style: solid;padding: 0;border-width: 0 0 1px;' align='left' valign='top'></td></tr></table>";
218
+
219
+ return $submissionHeader;
220
+ }
221
+
222
+ /**
223
+ * Builds a row containing a form field in the submission section for the lead email
224
+ * @param object $field
225
+ * @return string
226
+ */
227
+ function build_submission_row ( $field )
228
+ {
229
+ $submissionRow = "\r\n";
230
+
231
+ // Form Submission Row open
232
+ $submissionRow .= "<table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr align='left' style='vertical-align: top; text-align: left; padding: 0;'><td align='left' valign='top' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;border-bottom-width: 1px;border-bottom-color: #c9e1f3;border-bottom-style: solid;padding: 10px 20px;'>";
233
+
234
+ // Form Submission Label
235
+ $submissionRow .= "<p class='form-field-label' style='color: #222222; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-align: left; line-height: 19px; font-size: 12px; margin: 0; padding: 0;' align='left'>" . $field['label'] . "</p>";
236
+
237
+ // Form Submission Value
238
+ $submissionRow .= "<p class='form-field' style='color: #222222; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; text-align: left; line-height: 19px; font-size: 14px; margin: 0; padding: 3px 0 0;' align='left'>" . ( $field['value'] ? $field['value'] : 'not provided' ) . "</p>";
239
+
240
+ // Form Submission Row close
241
+ $submissionRow .= "<td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;border-bottom-color: #c9e1f3;border-bottom-style: solid;padding: 0;border-width: 0 0 1px;' align='left' valign='top'></td></tr></table>";
242
+
243
+ return $submissionRow;
244
+ }
245
+
246
+ /**
247
+ * Builds the orange session heading for the lead email
248
+ * @param object $session
249
+ * @return string
250
+ */
251
+ function build_history_header ( $session )
252
+ {
253
+ // @EMAIL - Use these variables to construct the heading for a specfic session
254
+ $session_header = $session[0]->pageview_day;
255
+ $session_start_time = str_replace(array('PM', 'AM'), array('pm', 'am'), str_replace($session[0]->pageview_day . " ", "", $session[0]->pageview_date));
256
+ $session_end_time = str_replace(array('PM', 'AM'), array('pm', 'am'), str_replace($session[0]->pageview_day . " ", "", $session[count($session)-1]->pageview_date));
257
+ $session_num_pages = ( count($session) != 1 ? count($session) . ' pages' : '1 page' ); // Fix plural on page/pages
258
+ $session_time_string = $session_start_time;
259
+ $session_source = $session[0]->pageview_source;
260
+
261
+ // Append the end time if it doesn't match the start time
262
+ if ( $session_start_time != $session_end_time )
263
+ {
264
+ $session_time_string .= " - " . $session_end_time;
265
+ }
266
+
267
+ $historyHeader = "\r\n";
268
+
269
+ // History section header open
270
+ $historyHeader .= "<table class='twelve columns' style='border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 580px; margin: 0 auto; padding: 0;'><tr style='vertical-align: top; text-align: left; padding: 0;' align='left'><td class='section-header' style='word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; border-bottom-width: 1px; border-bottom-style: solid; background: #fdd8c0; border-bottom-color: #fdd8c0; padding: 15px 20px;' align='left' bgcolor='#fdd8c0' valign='top'>";
271
+
272
+ // History section header content
273
+ $historyHeader .= "<h3 style='color: #a43700; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-align: left; line-height: 1.3; word-break: normal; font-size: 16px; margin: 0; padding: 0;' align='left'>Viewed " . $session_num_pages . " - " . $session_header . " " . $session_time_string . "</h3>";
274
+
275
+ // History section header close
276
+ $historyHeader .= "</td><td class='expander' style='word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; visibility: hidden; width: 0px; padding:0;' align='left' valign='top'></td></tr></table>";
277
+
278
+ // Source section open
279
+ $historyHeader .= "<table class='twelve columns' style='border-spacing: 0; border-collapse: collapse; vertical-align: top; text-align: left; width: 580px; margin: 0 auto; padding: 0;'><tr style='vertical-align: top; text-align: left; padding: 0;' align='left'><td style='word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; border-bottom-width: 1px; border-bottom-color: #fdd8c0; border-bottom-style: solid; padding: 10px 20px;' align='left' valign='top'>";
280
+
281
+ // Source section content
282
+ // If source isn't set, set it to direct
283
+ if ( $session_source )
284
+ {
285
+ $session_source_text .= "<a href='" . $session_source . "' style='color: #2ba6cb; text-decoration: none !important;'>" . $session_source . "</a>";
286
+ }
287
+ else
288
+ {
289
+ $session_source_text = "Direct";
290
+
291
+ }
292
+
293
+ $historyHeader .= "<p class='form-field' style='color: #222222; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; text-align: left; line-height: 19px; font-size: 14px; margin: 0; padding: 3px 0 0;' align='left'><span class='traffic-source' style='font-weight: bold; color: #a43700;'>Traffic Source: </span>" . $session_source_text . "</p>";
294
+
295
+ // Source section close
296
+ $historyHeader .= "</td><td class='expander' style='word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; visibility: hidden; width: 0px; border-bottom-color: #fdd8c0; border-bottom-style: solid; padding: 0; border: 0;' align='left' valign='top'></td></tr></table>";
297
+
298
+ return $historyHeader;
299
+ }
300
+
301
+ /**
302
+ * Builds a row containing a page view for the lead email
303
+ * @param object $pageview
304
+ * @return string
305
+ */
306
+ function build_history_row ( $pageview )
307
+ {
308
+ $historyRow = "\r\n";
309
+
310
+ $historyRow .= "<table class='twelve columns' style='border-spacing: 0; border-collapse: collapse; vertical-align: top;text-align: left; width: 580px; margin: 0 auto; padding: 0;'>";
311
+ $historyRow .= "<tr style='vertical-align: top;text-align: left;padding: 0;' align='left'>";
312
+ $historyRow .= "<td align='left' valign='top' style='border-bottom-color: #fdd8c0; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; border-bottom-width: 1px; border-bottom-style: solid; padding: 10px 20px;'>";
313
+ $historyRow .= "<a href='" . $pageview->pageview_url . "'>" . ( str_replace('&nbsp;)','', trim($pageview->pageview_title)) ? $pageview->pageview_title : '(blank page title tag)' ) . "</a>";
314
+ $historyRow .= "</td>";
315
+ $historyRow .= "<td class='expander' style='word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; border-collapse: collapse !important; vertical-align: top; text-align: left; visibility: hidden; width: 0px; padding:0;' align='left' valign='top'></td>";
316
+ $historyRow .= "</tr>";
317
+ $historyRow .= "</table>";
318
+
319
+ return $historyRow;
320
+ }
321
+
322
+ /**
323
+ * Builds the View All Contacts button for the lead email
324
+ * @return string
325
+ */
326
+ function build_button_row ( $lead_id )
327
+ {
328
+ $buttonRow = "\r\n";
329
+ $contactViewUrl = get_bloginfo('wpurl') . "/wp-admin/admin.php?page=leadin_contacts&action=view&lead=" . $lead_id;
330
+
331
+ $buttonRow .= "<table class='row section' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;display: block;margin-top: 20px;padding: 0px;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 0 0px 0 0;' align='left' valign='top'><table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;padding: 10px 20px;' align='left' valign='top'><table class='button round' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;overflow: hidden;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;display: block;width: auto !important;font-weight: bold;text-decoration: none;font-family: Helvetica, Arial, sans-serif;color: white;font-size: 16px;-webkit-border-radius: 500px;-moz-border-radius: 500px;border-radius: 500px;background: #f88e4b;padding: 10px 20px;border: 1px solid #f6601d;' align='center' bgcolor='#f88e4b' valign='top'>";
332
+ $buttonRow .="<a href='" . $contactViewUrl . "' style='color: white !important; text-decoration: none !important; font-family: Helvetica, Arial, sans-serif;'>View Contact History</a>";
333
+ $buttonRow .= "</td></tr></table></td><td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;padding: 0;border: 0;' align='left' valign='top'></td></tr></table></td></tr></table>";
334
+
335
+ return $buttonRow;
336
+ }
337
+
338
+ /**
339
+ * Sends the subscription confirmation email
340
+ *
341
+ * @param object history from get_lead_history()
342
+ * @return bool $email_sent Whether the email contents were sent successfully. A true return value does not automatically mean that the user received the email successfully. It just only means that the method used was able to process the request without any errors.
343
+ */
344
+ function send_subscriber_confirmation_email ( $history )
345
+ {
346
+ // Get email from plugin settings, if none set, use admin email
347
+ $options = get_option('leadin_options');
348
+ $leadin_email = ( $options['li_email'] ? $options['li_email'] : get_bloginfo('admin_email') ); // Get email from plugin settings, if none set, use admin email
349
+ $site_name = get_bloginfo('name');
350
+ $site_url = get_bloginfo('wpurl');
351
+
352
+ // @EMAIL - Use this variable to concatenate your HTML
353
+ $body = "";
354
+
355
+ // Email Base open
356
+ $body .= "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'><html xmlns='http://www.w3.org/1999/xhtml' xmlns='http://www.w3.org/1999/xhtml'><head><meta http-equiv='Content-Type' content='text/html;charset=utf-8'/><meta name='viewport' content='width=device-width'/></head><body style='width: 100% !important;-webkit-text-size-adjust: 100%;-ms-text-size-adjust: 100%;color: #222222;display: block;font-family: Helvetica, Arial, sans-serif;font-weight: normal;text-align: left;line-height: 19px;font-size: 14px;margin: 0;padding: 0;'><style type='text/css'>a:hover{color: #2795b6 !important;}a:active{color: #2795b6 !important;}a:visited{color: #2ba6cb !important;}h1 a:active{color: #2ba6cb !important;}h2 a:active{color: #2ba6cb !important;}h3 a:active{color: #2ba6cb !important;}h4 a:active{color: #2ba6cb !important;}h5 a:active{color: #2ba6cb !important;}h6 a:active{color: #2ba6cb !important;}h1 a:visited{color: #2ba6cb !important;}h2 a:visited{color: #2ba6cb !important;}h3 a:visited{color: #2ba6cb !important;}h4 a:visited{color: #2ba6cb !important;}h5 a:visited{color: #2ba6cb !important;}h6 a:visited{color: #2ba6cb !important;}.button:hover table td{background: #2795b6 !important;}.tiny-button:hover table td{background: #2795b6 !important;}.small-button:hover table td{background: #2795b6 !important;}.medium-button:hover table td{background: #2795b6 !important;}.large-button:hover table td{background: #2795b6 !important;}.button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.tiny-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.tiny-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.tiny-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.small-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.small-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.small-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.medium-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.medium-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.medium-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.large-button:hover{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.large-button:active{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.large-button:visited{color: white !important;font-family: Helvetica, Arial, sans-serif;text-decoration: none;}.secondary:hover table td{background: #d0d0d0 !important;}.success:hover table td{background: #457a1a !important;}.alert:hover table td{background: #970b0e !important;}@media only screen and (max-width: 600px){table[class='body'] img{width: auto !important;height: auto !important;}table[class='body'] .container{width: 95% !important;}table[class='body'] .row{width: 100% !important;display: block !important;}table[class='body'] .wrapper{display: block !important;padding-right: 0 !important;}table[class='body'] .columns{table-layout: fixed !important;float: none !important;width: 100% !important;padding-right: 0px !important;padding-left: 0px !important;display: block !important;}table[class='body'] .column{table-layout: fixed !important;float: none !important;width: 100% !important;padding-right: 0px !important;padding-left: 0px !important;display: block !important;}table[class='body'] .wrapper.first .columns{display: table !important;}table[class='body'] .wrapper.first .column{display: table !important;}table[class='body'] table.columns td{width: 100%;}table[class='body'] table.column td{width: 100%;}table[class='body'] td.offset-by-one{padding-left: 0 !important;}table[class='body'] td.offset-by-two{padding-left: 0 !important;}table[class='body'] td.offset-by-three{padding-left: 0 !important;}table[class='body'] td.offset-by-four{padding-left: 0 !important;}table[class='body'] td.offset-by-five{padding-left: 0 !important;}table[class='body'] td.offset-by-six{padding-left: 0 !important;}table[class='body'] td.offset-by-seven{padding-left: 0 !important;}table[class='body'] td.offset-by-eight{padding-left: 0 !important;}table[class='body'] td.offset-by-nine{padding-left: 0 !important;}table[class='body'] td.offset-by-ten{padding-left: 0 !important;}table[class='body'] td.offset-by-eleven{padding-left: 0 !important;}table[class='body'] .expander{width: 9999px !important;}table[class='body'] .hide-for-small{display: none !important;}table[class='body'] .show-for-desktop{display: none !important;}table[class='body'] .show-for-small{display: inherit !important;}table[class='body'] .hide-for-desktop{display: inherit !important;}table[class='body'] .container.main{width: 100% !important;}}</style><table class='body' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;height: 100%;width: 100%;padding: 0;'><tr align='left' style='vertical-align: top; text-align: left; padding: 0;'><td class='center' align='center' valign='top' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;padding: 0 0 20px;'><center style='width: 100%;'>";
357
+
358
+ // Email Header open
359
+ $body .= "<table class='row header' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;padding: 0px;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='center' align='center' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;padding: 0;' valign='top'><center style='width: 100%;'><table class='container' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: inherit;width: 580px;margin:0 auto 10px auto; padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 10px 0px 0px;' align='left' valign='top'><table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='two sub-columns' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;width: 100% !important;padding: 0px 0px 10px 0px;' align='left' valign='top'>";
360
+
361
+ $body .= "<h1 class='lead-name' style='color: #222222; display: block; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-align: left; line-height: 1.3; word-break: normal; font-size: 20px; margin: 0; padding: 0;' align='left'>" . $site_name . "</h1>";
362
+
363
+ // Email Header close
364
+ $body .= "</td><td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;padding: 0;' align='left' valign='top'></td></tr></table></td></tr></table></center></td></tr></table>";
365
+
366
+ $body .= "<table class='row header' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;padding: 0px;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='center' align='center' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;padding: 0;' valign='top'><center style='width: 100%;'><table class='container' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: inherit;width: 580px;margin:0 auto 10px auto; padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 10px 0px 0px;' align='left' valign='top'><table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'>";
367
+
368
+ $body .= "<tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td>";
369
+ $body .= "<td style='padding: 0px 0px 10px 0px;'>Your subscription to <i><a href='" . $site_url . "'>" . $site_name . "</a></i> has been confirmed.</td>";
370
+ $body .= "</tr>";
371
+
372
+ $body .= "<tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td>";
373
+ $body .= "<td style='padding: 10px 0px 20px 0px;'>Just so you have it, here is a copy of the information you submitted to us...</td>";
374
+ $body .= "</tr>";
375
+
376
+ $body .= "</table>";
377
+
378
+ // Main container open
379
+ $body .= "<table class='container main' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: inherit;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;padding: 0;' align='left' valign='top'>";
380
+
381
+ // Form Submission section open
382
+ $body .= "<table class='row section form-submission' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;display: block;background: #deedf8;padding: 0px;' bgcolor='#deedf8'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 0 0px 0 0;' align='left' valign='top'>";
383
+
384
+ // Form Submission section header
385
+ $body .= $this->build_submission_header($history->submission, TRUE);
386
+
387
+ // Form Submission Rows
388
+ $fields = json_decode(stripslashes($history->submission->form_fields), true);
389
+
390
+ foreach ( $fields as $field )
391
+ {
392
+ $body .= $this->build_submission_row($field);
393
+ }
394
+
395
+ // Form Submission Section Close
396
+ $body .= "</td></tr></table>";
397
+
398
+ // Build [you may contact us at:] row
399
+ $body .= "<table class='row section' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;display: block;margin-top: 20px;padding: 0px;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 0 0px 0 0;' align='left' valign='top'><table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;padding: 0px;' align='left' valign='top'><table class='button round' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;overflow: hidden;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td>";
400
+ $body .="You may also contact us at:<br/><a href='mailto:" . $leadin_email . "'>" . $leadin_email . "</a>";
401
+ $body .= "</td></tr></table></td><td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;padding: 0;border: 0;' align='left' valign='top'></td></tr></table></td></tr></table>";
402
+
403
+ // Build Powered by LeadIn row
404
+ $body .= "<table class='row section' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;position: relative;display: block;margin-top: 20px;padding: 0px;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td class='wrapper last' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;position: relative;padding: 0 0px 0 0;' align='left' valign='top'><table class='twelve columns' style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 580px;margin: 0 auto;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='padding: 10px 20px;' align='left' valign='top'><table style='border-spacing: 0;border-collapse: collapse;vertical-align: top;text-align: left;width: 100%;overflow: hidden;padding: 0;'><tr style='vertical-align: top;text-align: left;padding: 0;' align='left'><td style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: center;display: block;width: auto !important;font-size: 16px;padding: 10px 20px;' align='center' valign='top'>";
405
+ $body .="<div style='font-size: 11px; color: #888; padding: 0 0 5px 0;'>Powered by</div><a href='http://leadin.com/pop-subscribe-form-plugin-wordpress/'><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>";
406
+ $body .= "</td></tr></table></td><td class='expander' style='word-break: break-word;-webkit-hyphens: auto;-moz-hyphens: auto;hyphens: auto;border-collapse: collapse !important;vertical-align: top;text-align: left;visibility: hidden;width: 0px;padding: 0;border: 0;' align='left' valign='top'></td></tr></table></td></tr></table>";
407
+
408
+ // @EMAIL - end form section
409
+
410
+ // Email Base close
411
+ $body .= '</center></td></tr></table></body></html>';
412
+
413
+ // Each line in an email can only be 998 characters long, so lines need to be broken with a wordwrap
414
+ $body = wordwrap($body, 900, "\r\n");
415
+
416
+ $headers = "From: LeadIn <team@leadin.com>\r\n";
417
+ $headers.= "Reply-To: LeadIn <team@leadin.com>\r\n";
418
+ $headers.= "X-Mailer: PHP/" . phpversion()."\r\n";
419
+ $headers.= "MIME-Version: 1.0\r\n";
420
+ $headers.= "Content-type: text/html; charset=utf-8\r\n";
421
+
422
+ $subject = $site_name . ': Subscription Confirmed';
423
+
424
+ $email_sent = wp_mail($history->lead->lead_email, $subject, $body, $headers);
425
+ return $email_sent;
426
+ }
427
+ }
428
+ ?>
inc/class-leadin-options.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 ADDED
@@ -0,0 +1,312 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( !defined('LEADIN_PLUGIN_VERSION') )
4
+ {
5
+ header( 'HTTP/1.0 403 Forbidden' );
6
+ die;
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
+ */
14
+ function leadin_check_merged_contact ()
15
+ {
16
+ global $wpdb;
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);
27
+ $wpdb->query($q);
28
+
29
+ // One final update to set all the previous submissions to the new hashkey
30
+ $q = $wpdb->prepare("UPDATE li_submissions SET lead_hashkey = %s WHERE lead_hashkey = %s", $row->hashkey, $stale_hash);
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);
44
+ die();
45
+ }
46
+ else
47
+ {
48
+ echo json_encode(FALSE);
49
+ die();
50
+ }
51
+ }
52
+
53
+ add_action('wp_ajax_leadin_check_merged_contact', 'leadin_check_merged_contact'); // Call when user logged in
54
+ add_action('wp_ajax_nopriv_leadin_check_merged_contact', 'leadin_check_merged_contact'); // Call when user is not logged in
55
+
56
+ /**
57
+ * Inserts a new page view for a lead in li_pageviews
58
+ *
59
+ * @return int
60
+ */
61
+ function leadin_log_pageview ()
62
+ {
63
+ global $wpdb;
64
+
65
+ $hash = $_POST['li_id'];
66
+ $title = htmlentities($_POST['li_title']);
67
+ $url = $_POST['li_url'];
68
+ $source = ( isset($_POST['li_referrer']) ? $_POST['li_referrer'] : '' );
69
+ $last_visit = ( isset($_POST['li_last_visit']) ? $_POST['li_last_visit'] : 0 );
70
+
71
+ $result = $wpdb->insert(
72
+ 'li_pageviews',
73
+ array(
74
+ 'lead_hashkey' => $hash,
75
+ 'pageview_title' => $title,
76
+ 'pageview_url' => $url,
77
+ 'pageview_source' => $source,
78
+ 'pageview_session_start' => ( !$last_visit ? 1 : 0 )
79
+ ),
80
+ array(
81
+ '%s', '%s', '%s', '%s'
82
+ )
83
+ );
84
+
85
+ return $result;
86
+ }
87
+
88
+ add_action('wp_ajax_leadin_log_pageview', 'leadin_log_pageview'); // Call when user logged in
89
+ add_action('wp_ajax_nopriv_leadin_log_pageview', 'leadin_log_pageview'); // Call when user is not logged in
90
+
91
+ /**
92
+ * Inserts a new lead into li_leads on first visit
93
+ *
94
+ * @return int
95
+ */
96
+ function leadin_insert_lead ()
97
+ {
98
+ global $wpdb;
99
+
100
+ $hashkey = $_POST['li_id'];
101
+ $ipaddress = $_SERVER['REMOTE_ADDR'];
102
+ $source = ( isset($_POST['li_referrer']) ? $_POST['li_referrer'] : '' );
103
+
104
+ $result = $wpdb->insert(
105
+ 'li_leads',
106
+ array(
107
+ 'hashkey' => $hashkey,
108
+ 'lead_ip' => $ipaddress,
109
+ 'lead_source' => $source
110
+ ),
111
+ array(
112
+ '%s', '%s', '%s'
113
+ )
114
+ );
115
+
116
+ return $result;
117
+ }
118
+
119
+ add_action('wp_ajax_leadin_insert_lead', 'leadin_insert_lead'); // Call when user logged in
120
+ add_action('wp_ajax_nopriv_leadin_insert_lead', 'leadin_insert_lead'); // Call when user is not logged in
121
+
122
+ /**
123
+ * Inserts a new form submisison into the li_submissions table and ties to the submission to a row in li_leads
124
+ *
125
+ * @return int
126
+ */
127
+ function leadin_insert_form_submission ()
128
+ {
129
+ global $wpdb;
130
+
131
+ $submission_hash = $_POST['li_submission_id'];
132
+ $hashkey = $_POST['li_id'];
133
+ $page_title = htmlentities($_POST['li_title']);
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
+ }
150
+
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
169
+ if ( count($existing_contacts) )
170
+ {
171
+ for ( $i = 0; $i < count($existing_contacts); $i++ )
172
+ {
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
+
182
+ // Check on each existing lead if the lead_status is comment. If it is, save the status to override the new lead's status
183
+ if ( $existing_contacts[$i]->lead_status == 'comment' && $existing_contact_status == 'lead' )
184
+ $existing_contact_status = 'comment';
185
+
186
+ // Check on each existing lead if the lead_status is subscribe. If it is, save the status to override the new lead's status
187
+ if ( $existing_contacts[$i]->lead_status == 'subscribe' && ($existing_contact_status == 'lead' || $existing_contact_status == 'comment') )
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);
194
+
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'
217
+ )
218
+ );
219
+
220
+ $contact_status = $submission_type;
221
+
222
+ // Override the status because comment is further down the funnel than lead
223
+ if ( $contact->lead_status == 'comment' && $submission_type == 'lead' )
224
+ $contact_status = 'comment';
225
+ // Override the status because subscribe is further down the funnel than lead and comment
226
+ else if ( $contact->lead_status == 'subscribe' && ($submission_type == 'lead' || $submission_type == 'comment') )
227
+ $contact_status = 'subscribe';
228
+
229
+ // Override the status with the merged contacts status if the children have a status further down the funnel
230
+ if ( $existing_contact_status == 'comment' && $submission_type == 'lead' )
231
+ $contact_status = 'comment';
232
+ else if ( $existing_contact_status == 'subscribe' && ($submission_type == 'lead' || $submission_type == 'comment') )
233
+ $contact_status = 'subscribe';
234
+
235
+ // Update the contact with the new email, status and merged hashkeys
236
+ $q = $wpdb->prepare("UPDATE li_leads SET lead_email = %s, lead_status = %s, merged_hashkeys = %s WHERE hashkey = %s", $email, $contact_status, $existing_contact_hashkeys, $hashkey);
237
+ $rows_updated = $wpdb->query($q);
238
+
239
+ // Send the contact email
240
+ $li_emailer = new LI_Emailer();
241
+ $li_emailer->send_new_lead_email($hashkey);
242
+
243
+ return $rows_updated;
244
+ }
245
+ }
246
+
247
+ add_action('wp_ajax_leadin_insert_form_submission', 'leadin_insert_form_submission'); // Call when user logged in
248
+ add_action('wp_ajax_nopriv_leadin_insert_form_submission', 'leadin_insert_form_submission'); // Call when user is not logged in
249
+
250
+
251
+ /**
252
+ * Checks the lead status of the current visitor
253
+ *
254
+ */
255
+ function leadin_check_visitor_status ()
256
+ {
257
+ global $wpdb;
258
+
259
+ $hash = $_POST['li_id'];
260
+
261
+ $q = $wpdb->prepare("SELECT lead_status FROM li_leads WHERE hashkey = %s", $hash);
262
+ $lead_status = $wpdb->get_var($q);
263
+
264
+ if ( isset($lead_status) )
265
+ {
266
+ echo json_encode($lead_status);
267
+ die();
268
+ }
269
+ else
270
+ {
271
+ echo json_encode(FALSE);
272
+ die();
273
+ }
274
+ }
275
+
276
+ add_action('wp_ajax_leadin_check_visitor_status', 'leadin_check_visitor_status'); // Call when user logged in
277
+ add_action('wp_ajax_nopriv_leadin_check_visitor_status', 'leadin_check_visitor_status'); // Call when user is not logged in
278
+
279
+
280
+ /**
281
+ * Grabs the heading for the subscribe widget from the options
282
+ *
283
+ */
284
+ function get_leadin_subscribe_heading ()
285
+ {
286
+ echo json_encode('Subscribe because it worked!');
287
+ die();
288
+
289
+ /*global $wpdb;
290
+
291
+ $hash = $_POST['li_id'];
292
+
293
+ $q = $wpdb->prepare("SELECT lead_status FROM li_leads WHERE hashkey = %s", $hash);
294
+ $lead_status = $wpdb->get_var($q);
295
+
296
+ if ( isset($lead_status) )
297
+ {
298
+ echo json_encode($lead_status);
299
+ die();
300
+ }
301
+ else
302
+ {
303
+ echo json_encode(FALSE);
304
+ die();
305
+ }*/
306
+
307
+
308
+ }
309
+
310
+ add_action('wp_ajax_get_leadin_subscribe_heading', 'get_leadin_subscribe_heading'); // Call when user logged in
311
+ add_action('wp_ajax_nopriv_get_leadin_subscribe_heading', 'get_leadin_subscribe_heading'); // Call when user is not logged in
312
+ ?>
inc/leadin-functions.php ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( !defined('LEADIN_PLUGIN_VERSION') )
4
+ {
5
+ header('HTTP/1.0 403 Forbidden');
6
+ die;
7
+ }
8
+
9
+ /**
10
+ * Looks for a GET/POST value and echos if present. If nothing is set, echos blank
11
+ *
12
+ * @param string
13
+ * @return null
14
+ */
15
+ function print_submission_val ( $url_param )
16
+ {
17
+ if ( isset($_GET[$url_param]) )
18
+ {
19
+ return $_GET[$url_param];
20
+ }
21
+
22
+ if ( isset($_POST[$url_param]) )
23
+ {
24
+ return $_POST[$url_param];
25
+ }
26
+
27
+ return '';
28
+ }
29
+
30
+ /**
31
+ * Updates an option in the multi-dimensional option array
32
+ *
33
+ * @param string $option option_name in wp_options
34
+ * @param string $option_key key for array
35
+ * @param string $option new value for array
36
+ *
37
+ * @return bool True if option value has changed, false if not or if update failed.
38
+ */
39
+ function leadin_update_option ( $option, $option_key, $new_value )
40
+ {
41
+ $options_array = get_option($option);
42
+
43
+ if ( isset($options_array[$option_key]) )
44
+ {
45
+ if ( $options_array[$option_key] == $new_value )
46
+ return false; // Don't update an option if it already is set to the value
47
+ }
48
+
49
+ if ( !is_array( $options_array ) ) {
50
+ $options_array = array();
51
+ }
52
+
53
+ $options_array[$option_key] = $new_value;
54
+
55
+ return update_option($option, $options_array);
56
+ }
57
+
58
+ /**
59
+ * Prints a number with a singular or plural label depending on number
60
+ *
61
+ * @param int
62
+ * @param string
63
+ * @param string
64
+ * @return string
65
+ */
66
+ function leadin_single_plural_label ( $number, $singular_label, $plural_label )
67
+ {
68
+ //Set number = 0 when the variable is blank
69
+ $number = ( !is_numeric($number) ? 0 : $number );
70
+
71
+ return ( $number != 1 ? $number . " $plural_label" : $number . " $singular_label" );
72
+ }
73
+
74
+ /**
75
+ * Get LeadIn user
76
+ *
77
+ * @return array
78
+ */
79
+ function leadin_get_current_user ()
80
+ {
81
+ global $wp_version;
82
+ global $current_user;
83
+
84
+ get_currentuserinfo();
85
+ $li_user_id = md5(get_bloginfo('wpurl'));
86
+
87
+ $li_options = get_option('leadin_options');
88
+
89
+ if ( isset($li_options['li_email']) ) {
90
+ $li_user_email = $li_options['li_email'];
91
+ }
92
+ else {
93
+ $li_user_email = $current_user->user_email;
94
+ }
95
+
96
+ $leadin_user = array(
97
+ 'user_id' => $li_user_id,
98
+ 'email' => $li_user_email,
99
+ 'alias' => $current_user->display_name,
100
+ 'wp_url' => get_bloginfo('wpurl'),
101
+ 'li_version' => LEADIN_PLUGIN_VERSION,
102
+ 'wp_version' => $wp_version
103
+ );
104
+
105
+ return $leadin_user;
106
+ }
107
+
108
+ /**
109
+ * Register LeadIn user
110
+ *
111
+ * @return bool
112
+ */
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']);
121
+ $mp->people->set( $leadin_user['user_id'], array(
122
+ '$email' => $leadin_user['email'],
123
+ '$wp-url' => $leadin_user['wp_url'],
124
+ '$wp-version' => $leadin_user['wp_version'],
125
+ '$li-version' => $leadin_user['li_version']
126
+ ));
127
+
128
+ // @push contact to HubSpot
129
+
130
+ $hs_context = array(
131
+ 'pageName' => 'Plugin Settings'
132
+ );
133
+
134
+ $hs_context_json = json_encode($hs_context);
135
+
136
+ //Need to populate these varilables with values from the form.
137
+ $str_post = "email=" . urlencode($leadin_user['email'])
138
+ . "&li_version=" . urlencode($leadin_user['li_version'])
139
+ . "&leadin_stage=Activated"
140
+ . "&li_user_id=" . urlencode($leadin_user['user_id'])
141
+ . "&website=" . urlencode($leadin_user['wp_url'])
142
+ . "&wp_version=" . urlencode($leadin_user['wp_version'])
143
+ . "&hs_context=" . urlencode($hs_context_json);
144
+
145
+ $endpoint = 'https://forms.hubspot.com/uploads/form/v2/324680/d93719d5-e892-4137-98b0-913efffae204';
146
+
147
+ $ch = @curl_init();
148
+ @curl_setopt($ch, CURLOPT_POST, true);
149
+ @curl_setopt($ch, CURLOPT_POSTFIELDS, $str_post);
150
+ @curl_setopt($ch, CURLOPT_URL, $endpoint);
151
+ @curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
152
+ @curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
153
+ $response = @curl_exec($ch); //Log the response from HubSpot as needed.
154
+ @curl_close($ch);
155
+ echo $response;
156
+
157
+ return true;
158
+ }
159
+
160
+ /**
161
+ * Send Mixpanel event when plugin is activated/deactivated
162
+ *
163
+ * @param bool
164
+ *
165
+ * @return bool
166
+ */
167
+ function leadin_track_plugin_registration_hook ( $activated )
168
+ {
169
+ if ($activated) {
170
+ leadin_register_user();
171
+ leadin_track_plugin_activity("Activated Plugin");
172
+ }
173
+ else {
174
+ leadin_track_plugin_activity("Deactivated Plugin");
175
+ }
176
+
177
+ return true;
178
+ }
179
+
180
+ /**
181
+ * Track plugin activity in MixPanel
182
+ *
183
+ * @param string
184
+ *
185
+ * @return array
186
+ */
187
+ function leadin_track_plugin_activity ( $activity_desc )
188
+ {
189
+ $leadin_user = leadin_get_current_user();
190
+
191
+ global $wp_version;
192
+ global $current_user;
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;
200
+ }
201
+
202
+ /**
203
+ * Logs a debug statement to /wp-content/debug.log
204
+ *
205
+ * @param string
206
+ */
207
+ function leadin_log_debug ( $message )
208
+ {
209
+ if ( WP_DEBUG === TRUE )
210
+ {
211
+ if ( is_array($message) || is_object($message) )
212
+ error_log(print_r($message, TRUE));
213
+ else
214
+ error_log($message);
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Deletes an element or elements from an array
220
+ *
221
+ * @param array
222
+ * @param wildcard
223
+ * @return array
224
+ */
225
+ function leadin_array_delete ( $array, $element )
226
+ {
227
+ if ( !is_array($element) )
228
+ $element = array($element);
229
+
230
+ return array_diff($array, $element);
231
+ }
232
+
233
+ /**
234
+ * Deletes an element or elements from an array
235
+ *
236
+ * @param array
237
+ * @param wildcard
238
+ * @return array
239
+ */
240
+ function leadin_get_value_by_key ( $key_value, $array )
241
+ {
242
+ foreach ( $array as $key => $value )
243
+ {
244
+ if ( is_array($value) && $value['label'] == $key_value )
245
+ return $value['value'];
246
+ }
247
+
248
+ return null;
249
+ }
250
+ ?>
leadin.php ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
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.6.0
7
+ Author: Andy Cook, Nelson Joyce
8
+ Author URI: http://leadin.com
9
+ License: GPL2
10
+ */
11
+
12
+ //=============================================
13
+ // Define Constants
14
+ //=============================================
15
+
16
+ if ( !defined('LEADIN_PATH') )
17
+ define('LEADIN_PATH', untrailingslashit(plugins_url('', __FILE__ )));
18
+
19
+ if ( !defined('LEADIN_PLUGIN_DIR') )
20
+ define('LEADIN_PLUGIN_DIR', untrailingslashit(dirname( __FILE__ )));
21
+
22
+ 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.4.3');
27
+
28
+ if ( !defined('LEADIN_PLUGIN_VERSION') )
29
+ define('LEADIN_PLUGIN_VERSION', '0.6.0');
30
+
31
+ if ( !defined('MIXPANEL_PROJECT_TOKEN') )
32
+ define('MIXPANEL_PROJECT_TOKEN', 'a9615503ec58a6bce2c646a58390eac1');
33
+
34
+
35
+ //=============================================
36
+ // Include Needed Files
37
+ //=============================================
38
+
39
+ require_once(LEADIN_PLUGIN_DIR . '/admin/leadin-admin.php');
40
+ require_once(LEADIN_PLUGIN_DIR . '/inc/class-emailer.php');
41
+ require_once(LEADIN_PLUGIN_DIR . '/inc/leadin-ajax-functions.php');
42
+ 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 . '/lib/mixpanel/Mixpanel.php');
46
+
47
+ //=============================================
48
+ // WPLeadIn Class
49
+ //=============================================
50
+ class WPLeadIn {
51
+
52
+ var $power_ups;
53
+
54
+ /**
55
+ * Class constructor
56
+ */
57
+ function __construct ()
58
+ {
59
+ //=============================================
60
+ // Hooks & Filters
61
+ //=============================================
62
+
63
+ // Activate + install LeadIn
64
+ register_activation_hook( __FILE__, array(&$this, 'add_leadin_defaults'));
65
+
66
+ // Deactivate LeadIn
67
+ register_deactivation_hook( __FILE__, array(&$this, 'deactivate_leadin'));
68
+
69
+ $this->power_ups = $this->get_available_power_ups();
70
+
71
+ add_action('plugins_loaded', array($this, 'leadin_update_check'));
72
+ add_filter('init', array($this, 'add_leadin_frontend_scripts'));
73
+
74
+ add_filter('plugin_action_links_' . plugin_basename(__FILE__), array(&$li_wp_admin, 'leadin_plugin_settings_link'));
75
+ add_action( 'admin_bar_menu', array($this, 'add_leadin_link_to_admin_bar'), 999 );
76
+
77
+ $li_wp_admin = new WPLeadInAdmin($this->power_ups);
78
+ }
79
+
80
+ /**
81
+ * Activate the plugin
82
+ */
83
+ function add_leadin_defaults ()
84
+ {
85
+ $li_options = get_option('leadin_options');
86
+
87
+ if ( ($li_options['li_installed'] != 1) || (!is_array($li_options)) )
88
+ {
89
+ $opt = array(
90
+ 'li_installed' => 1,
91
+ 'li_db_version' => LEADIN_DB_VERSION,
92
+ 'li_email' => get_bloginfo('admin_email'),
93
+ 'onboarding_complete' => 0,
94
+ 'ignore_settings_popup' => 0
95
+ );
96
+
97
+ update_option('leadin_options', $opt);
98
+ $this->leadin_db_install();
99
+ }
100
+
101
+ $leadin_active_power_ups = get_option('leadin_active_power_ups');
102
+
103
+ if ( !$leadin_active_power_ups )
104
+ {
105
+ $auto_activate = array(
106
+ 'contacts'
107
+ );
108
+
109
+ update_option('leadin_active_power_ups', serialize($auto_activate));
110
+ }
111
+
112
+ // 0.4.0 upgrade - Delete legacy db option version 0.4.0 (remove after beta testers upgrade)
113
+ if ( get_option('leadin_db_version') )
114
+ delete_option('leadin_db_version');
115
+
116
+ // 0.4.0 upgrade - Delete legacy options version 0.4.0 (remove after beta testers upgrade)
117
+ if ( $li_legacy_options = get_option('leadin_plugin_options') )
118
+ {
119
+ leadin_update_option('leadin_options', 'li_email', $li_legacy_options['li_email']);
120
+ delete_option('leadin_plugin_options');
121
+ }
122
+
123
+ leadin_track_plugin_registration_hook(TRUE);
124
+ }
125
+
126
+ /**
127
+ * Deactivate LeadIn plugin hook
128
+ */
129
+ function deactivate_leadin ()
130
+ {
131
+ leadin_track_plugin_registration_hook(FALSE);
132
+ }
133
+
134
+ //=============================================
135
+ // Database update
136
+ //=============================================
137
+
138
+ /**
139
+ * Creates or updates the LeadIn tables
140
+ */
141
+ function leadin_db_install ()
142
+ {
143
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
144
+
145
+ $sql = "
146
+ CREATE TABLE li_pageviews (
147
+ pageview_id int(11) unsigned NOT NULL AUTO_INCREMENT,
148
+ pageview_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
149
+ lead_hashkey varchar(16) NOT NULL,
150
+ pageview_title varchar(255) NOT NULL,
151
+ pageview_url text NOT NULL,
152
+ pageview_source text NOT NULL,
153
+ pageview_session_start int(1) NOT NULL,
154
+ PRIMARY KEY (pageview_id)
155
+ ) CHARACTER SET utf8 COLLATE utf8_general_ci;
156
+
157
+ CREATE TABLE li_leads (
158
+ lead_id int(11) unsigned NOT NULL AUTO_INCREMENT,
159
+ lead_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
160
+ hashkey varchar(16) NOT NULL,
161
+ lead_ip varchar(40) NOT NULL,
162
+ lead_source text NOT NULL,
163
+ lead_email varchar(255) NOT NULL,
164
+ lead_status SET( 'lead', 'comment', 'subscribe' ) NOT NULL DEFAULT 'lead',
165
+ merged_hashkeys text NOT NULL,
166
+ PRIMARY KEY (lead_id)
167
+ ) CHARACTER SET utf8 COLLATE utf8_general_ci;
168
+
169
+ CREATE TABLE li_submissions (
170
+ form_id int(11) unsigned NOT NULL AUTO_INCREMENT,
171
+ form_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
172
+ form_hashkey varchar(16) NOT NULL,
173
+ lead_hashkey varchar(16) NOT NULL,
174
+ form_page_title varchar(255) NOT NULL,
175
+ form_page_url text NOT NULL,
176
+ form_fields text NOT NULL,
177
+ form_type SET( 'lead', 'comment', 'subscribe' ) NOT NULL DEFAULT 'lead',
178
+ PRIMARY KEY (form_id)
179
+ ) CHARACTER SET utf8 COLLATE utf8_general_ci;";
180
+
181
+ dbDelta($sql);
182
+
183
+ leadin_update_option('leadin_options', 'li_db_version', LEADIN_DB_VERSION);
184
+ }
185
+
186
+ /**
187
+ * Checks the stored database version against the current data version + updates if needed
188
+ */
189
+ function leadin_update_check ()
190
+ {
191
+ global $wpdb;
192
+ $li_options = get_option('leadin_options');
193
+
194
+ // 0.4.0 upgrade - Delete legacy db option version 0.4.0 (remove after beta is launched)
195
+ if ( get_option('leadin_db_version') )
196
+ delete_option('leadin_db_version');
197
+
198
+ // 0.4.0 upgrade - Delete legacy options version 0.4.0 (remove after beta is launched)
199
+ if ( $li_legacy_options = get_option('leadin_plugin_options') )
200
+ {
201
+ leadin_update_option('leadin_options', 'li_email', $li_legacy_options['li_email']);
202
+ delete_option('leadin_plugin_options');
203
+ }
204
+
205
+ $leadin_active_power_ups = get_option('leadin_active_power_ups');
206
+
207
+ if ( !$leadin_active_power_ups )
208
+ {
209
+ $auto_activate = array(
210
+ 'contacts'
211
+ );
212
+
213
+ update_option('leadin_active_power_ups', serialize($auto_activate));
214
+ }
215
+
216
+ if ( isset($li_options['li_db_version']) )
217
+ {
218
+ if ( $li_options['li_db_version'] != LEADIN_DB_VERSION ) {
219
+ $this->leadin_db_install();
220
+ }
221
+ }
222
+
223
+ // 0.4.2 upgrade - After the DB installation converts the set structure from contact to lead, update all the blank contacts = leads
224
+ $q = $wpdb->prepare("UPDATE li_leads SET lead_status = 'lead' WHERE lead_status = 'contact' OR lead_status = ''", "");
225
+ $wpdb->query($q);
226
+
227
+ // 0.4.2 upgrade - After the DB installation converts the set structure from contact to lead, update all the blank form_type = leads
228
+ $q = $wpdb->prepare("UPDATE li_submissions SET form_type = 'lead' WHERE form_type = 'contact' OR form_type = ''", "");
229
+ $wpdb->query($q);
230
+ }
231
+
232
+ //=============================================
233
+ // Scripts & Styles
234
+ //=============================================
235
+
236
+ /**
237
+ * Adds front end javascript + initializes ajax object
238
+ */
239
+ function add_leadin_frontend_scripts ()
240
+ {
241
+ if ( !is_admin() )
242
+ {
243
+ wp_register_script('leadin', LEADIN_PATH . '/frontend/js/leadin.js', array ('jquery'), false, true);
244
+ wp_register_script('jquery.cookie', LEADIN_PATH . '/frontend/js/jquery.cookie.js', array ('jquery'), false, true);
245
+
246
+ wp_enqueue_script('leadin');
247
+ wp_enqueue_script('jquery.cookie');
248
+
249
+ wp_localize_script('leadin', 'li_ajax', array('ajax_url' => admin_url('admin-ajax.php')));
250
+ }
251
+ }
252
+
253
+ function add_leadin_link_to_admin_bar( $wp_admin_bar ) {
254
+ $args = array(
255
+ 'id' => 'leadin-admin-menu', // id of the existing child node (New > Post)
256
+ 'title' => '<span class="ab-icon"><img src="/wp-content/plugins/leadin/images/leadin-svg-icon.svg" style="height:16px; width:16px;"></span><span class="ab-label">LeadIn</span>', // alter the title of existing node
257
+ 'parent' => false, // set parent to false to make it a top level (parent) node
258
+ 'href' => get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_contacts',
259
+ 'meta' => array('title' => 'LeadIn')
260
+ );
261
+
262
+ $wp_admin_bar->add_node( $args );
263
+ }
264
+
265
+ /**
266
+ * List available Jetpack modules. Simply lists .php files in /modules/.
267
+ * Make sure to tuck away module "library" files in a sub-directory.
268
+ */
269
+ public static function get_available_power_ups( $min_version = false, $max_version = false ) {
270
+ static $power_ups = null;
271
+
272
+ if ( ! isset( $power_ups ) ) {
273
+ $files = WPLeadIn::glob_php( LEADIN_PLUGIN_DIR . '/power-ups' );
274
+
275
+ $power_ups = array();
276
+
277
+ foreach ( $files as $file ) {
278
+
279
+ if ( ! $headers = WPLeadIn::get_power_up($file) ) {
280
+ continue;
281
+ }
282
+
283
+ $power_up = new $headers['class']($headers['activated']);
284
+ $power_up->power_up_name = $headers['name'];
285
+ $power_up->menu_text = $headers['menu_text'];
286
+ $power_up->menu_link = $headers['menu_link'];
287
+ $power_up->slug = $headers['slug'];
288
+ $power_up->link_uri = $headers['uri'];
289
+ $power_up->description = $headers['description'];
290
+ $power_up->icon = $headers['icon'];
291
+ $power_up->permanent = $headers['permanent'];
292
+ $power_up->auto_activate = $headers['auto_activate'];
293
+ $power_up->activated = $headers['activated'];
294
+
295
+ array_push($power_ups, $power_up);
296
+ }
297
+ }
298
+
299
+ return $power_ups;
300
+ }
301
+
302
+ /**
303
+ * Extract a power-up's slug from its full path.
304
+ */
305
+ public static function get_power_up_slug ( $file ) {
306
+ return str_replace( '.php', '', basename( $file ) );
307
+ }
308
+
309
+ /**
310
+ * Generate a power-up's path from its slug.
311
+ */
312
+ public static function get_power_up_path ( $slug ) {
313
+ return LEADIN_PLUGIN_DIR . "/power-ups/$slug.php";
314
+ }
315
+
316
+ /**
317
+ * Load power-up data from power-up file. Headers differ from WordPress
318
+ * plugin headers to avoid them being identified as standalone
319
+ * plugins on the WordPress plugins page.
320
+ *
321
+ * @param $power_up The file path for the power-up
322
+ * @return $pu array of power-up attributes
323
+ */
324
+ public static function get_power_up ( $power_up )
325
+ {
326
+ $headers = array(
327
+ 'name' => 'Power-up Name',
328
+ 'class' => 'Power-up Class',
329
+ 'menu_text' => 'Power-up Menu Text',
330
+ 'menu_link' => 'Power-up Menu Link',
331
+ 'slug' => 'Power-up Slug',
332
+ 'uri' => 'Power-up URI',
333
+ 'description' => 'Power-up Description',
334
+ 'icon' => 'Power-up Icon',
335
+ 'introduced' => 'First Introduced',
336
+ 'auto_activate' => 'Auto Activate',
337
+ 'permanent' => 'Permanently Enabled',
338
+ 'power_up_tags' => 'Power-up Tags'
339
+ );
340
+
341
+ $file = WPLeadIn::get_power_up_path( WPLeadIn::get_power_up_slug( $power_up ) );
342
+ if ( ! file_exists( $file ) )
343
+ return false;
344
+
345
+ $pu = get_file_data( $file, $headers );
346
+
347
+ if ( empty( $pu['name'] ) )
348
+ return false;
349
+
350
+ $pu['activated'] = self::is_power_up_active($pu['slug']);
351
+
352
+ return $pu;
353
+ }
354
+
355
+ /**
356
+ * Returns an array of all PHP files in the specified absolute path.
357
+ * Equivalent to glob( "$absolute_path/*.php" ).
358
+ *
359
+ * @param string $absolute_path The absolute path of the directory to search.
360
+ * @return array Array of absolute paths to the PHP files.
361
+ */
362
+ public static function glob_php( $absolute_path ) {
363
+ $absolute_path = untrailingslashit( $absolute_path );
364
+ $files = array();
365
+ if ( ! $dir = @opendir( $absolute_path ) ) {
366
+ return $files;
367
+ }
368
+
369
+ while ( false !== $file = readdir( $dir ) ) {
370
+ if ( '.' == substr( $file, 0, 1 ) || '.php' != substr( $file, -4 ) ) {
371
+ continue;
372
+ }
373
+
374
+ $file = "$absolute_path/$file";
375
+
376
+ if ( ! is_file( $file ) ) {
377
+ continue;
378
+ }
379
+
380
+ $files[] = $file;
381
+ }
382
+
383
+ closedir( $dir );
384
+
385
+ return $files;
386
+ }
387
+
388
+ /**
389
+ * Check whether or not a LeadIn power-up is active.
390
+ *
391
+ * @param string $power_up The slug of a Jetpack module.
392
+ * @return bool
393
+ *
394
+ * @static
395
+ */
396
+ public static function is_power_up_active( $power_up_slug )
397
+ {
398
+ return in_array($power_up_slug, self::get_active_power_ups());
399
+ }
400
+
401
+ /**
402
+ * Get a list of activated modules as an array of module slugs.
403
+ */
404
+ public static function get_active_power_ups ()
405
+ {
406
+ $activated_power_ups = get_option('leadin_active_power_ups');
407
+ if ( $activated_power_ups )
408
+ return array_unique(unserialize($activated_power_ups));
409
+ else
410
+ return array();
411
+ }
412
+
413
+ public static function activate_power_up( $power_up_slug, $exit = TRUE )
414
+ {
415
+ if ( ! strlen( $power_up_slug ) )
416
+ return false;
417
+
418
+ // If it's already active, then don't do it again
419
+ $active = self::is_power_up_active($power_up_slug);
420
+ if ( $active )
421
+ return true;
422
+
423
+ $activated_power_ups = get_option('leadin_active_power_ups');
424
+
425
+ if ( $activated_power_ups )
426
+ {
427
+ $activated_power_ups = unserialize($activated_power_ups);
428
+ $activated_power_ups[] = $power_up_slug;
429
+ }
430
+ else
431
+ {
432
+ $activated_power_ups = array($power_up_slug);
433
+ }
434
+
435
+ update_option('leadin_active_power_ups', serialize($activated_power_ups));
436
+
437
+
438
+ if ( $exit )
439
+ {
440
+ exit;
441
+ }
442
+
443
+ }
444
+
445
+ public static function deactivate_power_up( $power_up_slug, $exit = TRUE )
446
+ {
447
+ if ( ! strlen( $power_up_slug ) )
448
+ return false;
449
+
450
+ // If it's already active, then don't do it again
451
+ $active = self::is_power_up_active($power_up_slug);
452
+ if ( ! $active )
453
+ return true;
454
+
455
+ $activated_power_ups = get_option('leadin_active_power_ups');
456
+
457
+ $power_ups_left = leadin_array_delete(unserialize($activated_power_ups), $power_up_slug);
458
+ update_option('leadin_active_power_ups', serialize($power_ups_left));
459
+
460
+ if ( $exit )
461
+ {
462
+ exit;
463
+ }
464
+
465
+ }
466
+ }
467
+
468
+ //=============================================
469
+ // LeadIn Init
470
+ //=============================================
471
+
472
+ global $leadin_wp;
473
+ global $li_wp_admin;
474
+ $leadin_wp = new WPLeadIn();
475
+
476
+
477
+ ?>
lib/mixpanel/Base/MixpanelBase.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
11
+ /**
12
+ * Default options that can be overridden via the $options constructor arg
13
+ * @var array
14
+ */
15
+ private $_defaults = array(
16
+ "max_batch_size" => 50, // the max batch size Mixpanel will accept is 50,
17
+ "max_queue_size" => 1000, // the max num of items to hold in memory before flushing
18
+ "debug" => false, // enable/disable debug mode
19
+ "consumer" => "curl", // which consumer to use
20
+ "host" => "api.mixpanel.com", // the host name for api calls
21
+ "events_endpoint" => "/track", // host relative endpoint for events
22
+ "people_endpoint" => "/engage", // host relative endpoint for people updates
23
+ "use_ssl" => true, // use ssl when available
24
+ "error_callback" => null // callback to use on consumption failures
25
+ );
26
+
27
+
28
+ /**
29
+ * An array of options to be used by the Mixpanel library.
30
+ * @var array
31
+ */
32
+ protected $_options = array();
33
+
34
+
35
+ /**
36
+ * Construct a new MixpanelBase object and merge custom options with defaults
37
+ * @param array $options
38
+ */
39
+ public function __construct($options = array()) {
40
+ $options = array_merge($this->_defaults, $options);
41
+ $this->_options = $options;
42
+ }
43
+
44
+
45
+ /**
46
+ * Log a message to PHP's error log
47
+ * @param $msg
48
+ */
49
+ protected function _log($msg) {
50
+ $arr = debug_backtrace();
51
+ $class = $arr[0]['class'];
52
+ $line = $arr[0]['line'];
53
+ error_log ( "[ $class - line $line ] : " . $msg );
54
+ }
55
+
56
+
57
+ /**
58
+ * Returns true if in debug mode, false if in production mode
59
+ * @return bool
60
+ */
61
+ protected function _debug() {
62
+ return array_key_exists("debug", $this->_options) && $this->_options["debug"] == true;
63
+ }
64
+
65
+ }
lib/mixpanel/ConsumerStrategies/AbstractConsumer.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/../Base/MixpanelBase.php");
3
+
4
+ /**
5
+ * Provides some base methods for use by a Consumer implementation
6
+ */
7
+ abstract class ConsumerStrategies_AbstractConsumer extends Base_MixpanelBase {
8
+
9
+ /**
10
+ * Creates a new AbstractConsumer
11
+ * @param array $options
12
+ */
13
+ function __construct($options = array()) {
14
+
15
+ parent::__construct($options);
16
+
17
+ if ($this->_debug()) {
18
+ $this->_log("Instantiated new Consumer");
19
+ }
20
+
21
+ }
22
+
23
+ /**
24
+ * Encode an array to be persisted
25
+ * @param array $params
26
+ * @return string
27
+ */
28
+ protected function _encode($params) {
29
+ return base64_encode(json_encode($params));
30
+ }
31
+
32
+ /**
33
+ * Handles errors that occur in a consumer
34
+ * @param $code
35
+ * @param $msg
36
+ */
37
+ protected function _handleError($code, $msg) {
38
+ if (isset($this->_options['error_callback'])) {
39
+ $handler = $this->_options['error_callback'];
40
+ call_user_func($handler, $code, $msg);
41
+ }
42
+
43
+ if ($this->_debug()) {
44
+ $arr = debug_backtrace();
45
+ $class = get_class($arr[0]['object']);
46
+ $line = $arr[0]['line'];
47
+ error_log ( "[ $class - line $line ] : " . print_r($msg, true) );
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Persist a batch of messages in whatever way the implementer sees fit
53
+ * @param array $batch an array of messages to consume
54
+ * @return boolean success or fail
55
+ */
56
+ abstract function persist($batch);
57
+ }
lib/mixpanel/ConsumerStrategies/CurlConsumer.php ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/AbstractConsumer.php");
3
+
4
+ /**
5
+ * Consumes messages and sends them to a host/endpoint using cURL
6
+ */
7
+ class ConsumerStrategies_CurlConsumer extends ConsumerStrategies_AbstractConsumer {
8
+
9
+ /**
10
+ * @var string the host to connect to (e.g. api.mixpanel.com)
11
+ */
12
+ protected $_host;
13
+
14
+
15
+ /**
16
+ * @var string the host-relative endpoint to write to (e.g. /engage)
17
+ */
18
+ protected $_endpoint;
19
+
20
+
21
+ /**
22
+ * @var int connect_timeout The number of seconds to wait while trying to connect. Default is 5 seconds.
23
+ */
24
+ protected $_connect_timeout;
25
+
26
+
27
+ /**
28
+ * @var int timeout The maximum number of seconds to allow cURL call to execute. Default is 30 seconds.
29
+ */
30
+ protected $_timeout;
31
+
32
+
33
+ /**
34
+ * @var string the protocol to use for the cURL connection
35
+ */
36
+ protected $_protocol;
37
+
38
+
39
+ /**
40
+ * @var bool|null true to fork the cURL process (using exec) or false to use PHP's cURL extension. false by default
41
+ */
42
+ protected $_fork = null;
43
+
44
+
45
+ /**
46
+ * Creates a new CurlConsumer and assigns properties from the $options array
47
+ * @param array $options
48
+ * @throws Exception
49
+ */
50
+ function __construct($options) {
51
+ parent::__construct($options);
52
+
53
+ $this->_host = $options['host'];
54
+ $this->_endpoint = $options['endpoint'];
55
+ $this->_connect_timeout = array_key_exists('connect_timeout', $options) ? $options['connect_timeout'] : 5;
56
+ $this->_timeout = array_key_exists('timeout', $options) ? $options['timeout'] : 30;
57
+ $this->_protocol = array_key_exists('use_ssl', $options) && $options['use_ssl'] == true ? "https" : "http";
58
+ $this->_fork = array_key_exists('fork', $options) ? ($options['fork'] == true) : false;
59
+
60
+ // ensure the environment is workable for the given settings
61
+ if ($this->_fork == true) {
62
+ $exists = function_exists('exec');
63
+ if (!$exists) {
64
+ throw new Exception('The "exec" function must exist to use the cURL consumer in "fork" mode. Try setting fork = false or use another consumer.');
65
+ }
66
+ $disabled = explode(', ', ini_get('disable_functions'));
67
+ $enabled = !in_array('exec', $disabled);
68
+ if (!$enabled) {
69
+ 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.');
70
+ }
71
+ } else {
72
+ if (!function_exists('curl_init')) {
73
+ 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.');
74
+ }
75
+ }
76
+ }
77
+
78
+
79
+ /**
80
+ * Write to the given host/endpoint using either a forked cURL process or using PHP's cURL extension
81
+ * @param array $batch
82
+ * @return bool
83
+ */
84
+ public function persist($batch) {
85
+ if (count($batch) > 0) {
86
+ $data = "data=" . $this->_encode($batch);
87
+ $url = $this->_protocol . "://" . $this->_host . $this->_endpoint;
88
+ if ($this->_fork) {
89
+ return $this->_execute_forked($url, $data);
90
+ } else {
91
+ return $this->_execute($url, $data);
92
+ }
93
+ } else {
94
+ return true;
95
+ }
96
+ }
97
+
98
+
99
+ /**
100
+ * Write using the cURL php extension
101
+ * @param $url
102
+ * @param $data
103
+ * @return bool
104
+ */
105
+ protected function _execute($url, $data) {
106
+ if ($this->_debug()) {
107
+ $this->_log("Making blocking cURL call to $url");
108
+ }
109
+
110
+ $ch = curl_init();
111
+ curl_setopt($ch, CURLOPT_URL, $url);
112
+ curl_setopt($ch, CURLOPT_HEADER, 0);
113
+ curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->_connect_timeout);
114
+ curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout);
115
+ curl_setopt($ch, CURLOPT_POST, 1);
116
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
117
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
118
+ $response = curl_exec($ch);
119
+ if (false === $response) {
120
+ $curl_error = curl_error($ch);
121
+ $curl_errno = curl_errno($ch);
122
+ curl_close($ch);
123
+ $this->_handleError($curl_errno, $curl_error);
124
+ return false;
125
+ } else {
126
+ curl_close($ch);
127
+ if (trim($response) == "1") {
128
+ return true;
129
+ } else {
130
+ $this->_handleError(0, $response);
131
+ return false;
132
+ }
133
+ }
134
+ }
135
+
136
+
137
+ /**
138
+ * Write using a forked cURL process
139
+ * @param $url
140
+ * @param $data
141
+ * @return bool
142
+ */
143
+ protected function _execute_forked($url, $data) {
144
+
145
+ if ($this->_debug()) {
146
+ $this->_log("Making forked cURL call to $url");
147
+ }
148
+
149
+ $exec = 'curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d ' . $data . ' "' . $url . '"';
150
+
151
+ if(!$this->_debug()) {
152
+ $exec .= " >/dev/null 2>&1 &";
153
+ }
154
+
155
+ exec($exec, $output, $return_var);
156
+
157
+ if ($return_var != 0) {
158
+ $this->_handleError($return_var, $output);
159
+ }
160
+
161
+ return $return_var == 0;
162
+ }
163
+
164
+ /**
165
+ * @return int
166
+ */
167
+ public function getConnectTimeout()
168
+ {
169
+ return $this->_connect_timeout;
170
+ }
171
+
172
+ /**
173
+ * @return string
174
+ */
175
+ public function getEndpoint()
176
+ {
177
+ return $this->_endpoint;
178
+ }
179
+
180
+ /**
181
+ * @return bool|null
182
+ */
183
+ public function getFork()
184
+ {
185
+ return $this->_fork;
186
+ }
187
+
188
+ /**
189
+ * @return string
190
+ */
191
+ public function getHost()
192
+ {
193
+ return $this->_host;
194
+ }
195
+
196
+ /**
197
+ * @return array
198
+ */
199
+ public function getOptions()
200
+ {
201
+ return $this->_options;
202
+ }
203
+
204
+ /**
205
+ * @return string
206
+ */
207
+ public function getProtocol()
208
+ {
209
+ return $this->_protocol;
210
+ }
211
+
212
+ /**
213
+ * @return int
214
+ */
215
+ public function getTimeout()
216
+ {
217
+ return $this->_timeout;
218
+ }
219
+
220
+
221
+ }
lib/mixpanel/ConsumerStrategies/FileConsumer.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/AbstractConsumer.php");
3
+ /**
4
+ * Consumes messages and writes them to a file
5
+ */
6
+ class ConsumerStrategies_FileConsumer extends ConsumerStrategies_AbstractConsumer {
7
+
8
+ /**
9
+ * @var string path to a file that we want to write the messages to
10
+ */
11
+ private $_file;
12
+
13
+
14
+ /**
15
+ * Creates a new FileConsumer and assigns properties from the $options array
16
+ * @param array $options
17
+ */
18
+ function __construct($options) {
19
+ parent::__construct($options);
20
+
21
+ // what file to write to?
22
+ $this->_file = array_key_exists("file", $options) ? $options['file'] : dirname(__FILE__)."/../../messages.txt";
23
+ }
24
+
25
+
26
+ /**
27
+ * Append $batch to a file
28
+ * @param array $batch
29
+ * @return bool
30
+ */
31
+ public function persist($batch) {
32
+ if (count($batch) > 0) {
33
+ return file_put_contents($this->_file, json_encode($batch)."\n", FILE_APPEND | LOCK_EX) !== false;
34
+ } else {
35
+ return true;
36
+ }
37
+ }
38
+ }
lib/mixpanel/ConsumerStrategies/SocketConsumer.php ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ require_once(dirname(__FILE__) . "/AbstractConsumer.php");
35
+
36
+ /**
37
+ * Consumes messages and writes them to host/endpoint using a persistent socket
38
+ */
39
+ class ConsumerStrategies_SocketConsumer extends ConsumerStrategies_AbstractConsumer {
40
+
41
+ /**
42
+ * @var string the host to connect to (e.g. api.mixpanel.com)
43
+ */
44
+ private $_host;
45
+
46
+
47
+ /**
48
+ * @var string the host-relative endpoint to write to (e.g. /engage)
49
+ */
50
+ private $_endpoint;
51
+
52
+
53
+ /**
54
+ * @var int connect_timeout the socket connection timeout in seconds
55
+ */
56
+ private $_connect_timeout;
57
+
58
+
59
+ /**
60
+ * @var string the protocol to use for the socket connection
61
+ */
62
+ private $_protocol;
63
+
64
+
65
+ /**
66
+ * @var resource holds the socket resource
67
+ */
68
+ private $_socket;
69
+
70
+ /**
71
+ * @var bool whether or not to wait for a response
72
+ */
73
+ private $_async;
74
+
75
+
76
+ /**
77
+ * Creates a new SocketConsumer and assigns properties from the $options array
78
+ * @param array $options
79
+ */
80
+ public function __construct($options = array()) {
81
+ parent::__construct($options);
82
+
83
+
84
+ $this->_host = $options['host'];
85
+ $this->_endpoint = $options['endpoint'];
86
+ $this->_connect_timeout = array_key_exists('connect_timeout', $options) ? $options['connect_timeout'] : 5;
87
+ $this->_async = array_key_exists('async', $options) && $options['async'] === false ? false : true;
88
+
89
+ if (array_key_exists('use_ssl', $options) && $options['use_ssl'] == true) {
90
+ $this->_protocol = "ssl";
91
+ $this->_port = 443;
92
+ } else {
93
+ $this->_protocol = "tcp";
94
+ $this->_port = 80;
95
+ }
96
+ }
97
+
98
+
99
+ /**
100
+ * Write using a persistent socket connection.
101
+ * @param array $batch
102
+ * @return bool
103
+ */
104
+ public function persist($batch) {
105
+
106
+ $socket = $this->_getSocket();
107
+ if (!is_resource($socket)) {
108
+ return false;
109
+ }
110
+
111
+ $data = "data=".$this->_encode($batch);
112
+
113
+ $body = "";
114
+ $body.= "POST ".$this->_endpoint." HTTP/1.1\r\n";
115
+ $body.= "Host: " . $this->_host . "\r\n";
116
+ $body.= "Content-Type: application/x-www-form-urlencoded\r\n";
117
+ $body.= "Accept: application/json\r\n";
118
+ $body.= "Content-length: " . strlen($data) . "\r\n";
119
+ $body.= "\r\n";
120
+ $body.= $data;
121
+
122
+ return $this->_write($socket, $body);
123
+ }
124
+
125
+
126
+ /**
127
+ * Return cached socket if open or create a new persistent socket
128
+ * @return bool|resource
129
+ */
130
+ private function _getSocket() {
131
+ if(is_resource($this->_socket)) {
132
+
133
+ if ($this->_debug()) {
134
+ $this->_log("Using existing socket");
135
+ }
136
+
137
+ return $this->_socket;
138
+ } else {
139
+
140
+ if ($this->_debug()) {
141
+ $this->_log("Creating new socket at ".time());
142
+ }
143
+
144
+ return $this->_createSocket();
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Attempt to open a new socket connection, cache it, and return the resource
150
+ * @param bool $retry
151
+ * @return bool|resource
152
+ */
153
+ private function _createSocket($retry = true) {
154
+ try {
155
+ $socket = pfsockopen($this->_protocol . "://" . $this->_host, $this->_port, $err_no, $err_msg, $this->_connect_timeout);
156
+
157
+ if ($this->_debug()) {
158
+ $this->_log("Opening socket connection to " . $this->_protocol . "://" . $this->_host . ":" . $this->_port);
159
+ }
160
+
161
+ if ($err_no != 0) {
162
+ $this->_handleError($err_no, $err_msg);
163
+ return $retry == true ? $this->_createSocket(false) : false;
164
+ } else {
165
+ // cache the socket
166
+ $this->_socket = $socket;
167
+ return $socket;
168
+ }
169
+
170
+ } catch (Exception $e) {
171
+ $this->_handleError($e->getCode(), $e->getMessage());
172
+ return $retry == true ? $this->_createSocket(false) : false;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Attempt to close and dereference a socket resource
178
+ */
179
+ private function _destroySocket() {
180
+ $socket = $this->_socket;
181
+ $this->_socket = null;
182
+ fclose($socket);
183
+ }
184
+
185
+
186
+ /**
187
+ * Write $data through the given $socket
188
+ * @param $socket
189
+ * @param $data
190
+ * @param bool $retry
191
+ * @return bool
192
+ */
193
+ private function _write($socket, $data, $retry = true) {
194
+
195
+ $bytes_sent = 0;
196
+ $bytes_total = strlen($data);
197
+ $socket_closed = false;
198
+ $success = true;
199
+ $max_bytes_per_write = 8192;
200
+
201
+ // if we have no data to write just return true
202
+ if ($bytes_total == 0) {
203
+ return true;
204
+ }
205
+
206
+ // try to write the data
207
+ while (!$socket_closed && $bytes_sent < $bytes_total) {
208
+
209
+ try {
210
+ $bytes = fwrite($socket, $data, $max_bytes_per_write);
211
+
212
+ if ($this->_debug()) {
213
+ $this->_log("Socket wrote ".$bytes." bytes");
214
+ }
215
+
216
+ // if we actually wrote data, then remove the written portion from $data left to write
217
+ if ($bytes > 0) {
218
+ $data = substr($data, $max_bytes_per_write);
219
+ }
220
+
221
+ } catch (Exception $e) {
222
+ $this->_handleError($e->getCode(), $e->getMessage());
223
+ $socket_closed = true;
224
+ }
225
+
226
+ if (isset($bytes) && $bytes) {
227
+ $bytes_sent += $bytes;
228
+ } else {
229
+ $socket_closed = true;
230
+ }
231
+ }
232
+
233
+ // create a new socket if the current one is closed and retry the message
234
+ if ($socket_closed) {
235
+
236
+ $this->_destroySocket();
237
+
238
+ if ($retry) {
239
+ if ($this->_debug()) {
240
+ $this->_log("Retrying socket write...");
241
+ }
242
+ $socket = $this->_getSocket();
243
+ if ($socket) return $this->_write($socket, $data, false);
244
+ }
245
+
246
+ return false;
247
+ }
248
+
249
+
250
+ // only wait for the response in debug mode or if we explicitly want to be synchronous
251
+ if ($this->_debug() || !$this->_async) {
252
+ $res = $this->handleResponse(fread($socket, 2048));
253
+ if ($res["status"] != "200") {
254
+ $this->_handleError($res["status"], $res["body"]);
255
+ $success = false;
256
+ }
257
+ }
258
+
259
+ return $success;
260
+ }
261
+
262
+
263
+ /**
264
+ * Parse the response from a socket write (only used for debugging)
265
+ * @param $response
266
+ * @return array
267
+ */
268
+ private function handleResponse($response) {
269
+
270
+ $lines = explode("\n", $response);
271
+
272
+ // extract headers
273
+ $headers = array();
274
+ foreach($lines as $line) {
275
+ $kvsplit = explode(":", $line);
276
+ if (count($kvsplit) == 2) {
277
+ $header = $kvsplit[0];
278
+ $value = $kvsplit[1];
279
+ $headers[$header] = trim($value);
280
+ }
281
+
282
+ }
283
+
284
+ // extract status
285
+ $line_one_exploded = explode(" ", $lines[0]);
286
+ $status = $line_one_exploded[1];
287
+
288
+ // extract body
289
+ $body = $lines[count($lines) - 1];
290
+
291
+ // if the connection has been closed lets kill the socket
292
+ if ($headers['Connection'] == "close") {
293
+ $this->_destroySocket();
294
+ if ($this->_debug()) {
295
+ $this->_log("Server told us connection closed so lets destroy the socket so it'll reconnect on next call");
296
+ }
297
+ }
298
+
299
+ $ret = array(
300
+ "status" => $status,
301
+ "body" => $body,
302
+ );
303
+
304
+ return $ret;
305
+ }
306
+
307
+
308
+ }
lib/mixpanel/Mixpanel.php ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
9
+ * create/update profiles.
10
+ *
11
+ * Architecture
12
+ * -------------
13
+ *
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.
21
+ *
22
+ * Options
23
+ * -------------
24
+ *
25
+ * <table width="100%" cellpadding="5">
26
+ * <tr>
27
+ * <th>Option</th>
28
+ * <th>Description</th>
29
+ * <th>Default</th>
30
+ * </tr>
31
+ * <tr>
32
+ * <td>max_queue_size</td>
33
+ * <td>The maximum number of items to buffer in memory before flushing</td>
34
+ * <td>1000</td>
35
+ * </tr>
36
+ * <tr>
37
+ * <td>debug</td>
38
+ * <td>Enable/disable debug mode</td>
39
+ * <td>false</td>
40
+ * </tr>
41
+ * <tr>
42
+ * <td>consumer</td>
43
+ * <td>The consumer to use for writing messages</td>
44
+ * <td>curl</td>
45
+ * </tr>
46
+ * <tr>
47
+ * <td>consumers</td>
48
+ * <td>An array of custom consumers in the format array(consumer_key => class_name)</td>
49
+ * <td>null</td>
50
+ * </tr>
51
+ * <tr>
52
+ * <td>host</td>
53
+ * <td>The host name for api calls (used by some consumers)</td>
54
+ * <td>api.mixpanel.com</td>
55
+ * </tr>
56
+ * <tr>
57
+ * <td>events_endpoint</td>
58
+ * <td>The endpoint for tracking events (relative to the host)</td>
59
+ * <td>/events</td>
60
+ * </tr>
61
+ * <tr>
62
+ * <td>people_endpoint</td>
63
+ * <td>The endpoint for making people updates (relative to the host)</td>
64
+ * <td>/engage</td>
65
+ * </tr>
66
+ * <tr>
67
+ * <td>use_ssl</td>
68
+ * <td>Tell the consumer whether or not to use ssl (when available)</td>
69
+ * <td>true</td>
70
+ * </tr>
71
+ * <tr>
72
+ * <td>error_callback</td>
73
+ * <td>The name of a function to be called on consumption failures</td>
74
+ * <td>null</td>
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>
87
+ *
88
+ * Example: Tracking an Event
89
+ * -------------
90
+ *
91
+ * $mp = Mixpanel::getInstance("MY_TOKEN");
92
+ *
93
+ * $mp->track("My Event");
94
+ *
95
+ * Example: Setting Profile Properties
96
+ * -------------
97
+ *
98
+ * $mp = Mixpanel::getInstance("MY_TOKEN", array("use_ssl" => false));
99
+ *
100
+ * $mp->people->set(12345, array(
101
+ * '$first_name' => "John",
102
+ * '$last_name' => "Doe",
103
+ * '$email' => "john.doe@example.com",
104
+ * '$phone' => "5555555555",
105
+ * 'Favorite Color' => "red"
106
+ * ));
107
+ *
108
+ */
109
+ class Mixpanel extends Base_MixpanelBase {
110
+
111
+
112
+ /**
113
+ * An instance of the MixpanelPeople class (used to create/update profiles)
114
+ * @var MixpanelPeople
115
+ */
116
+ public $people;
117
+
118
+
119
+ /**
120
+ * An instance of the MixpanelEvents class
121
+ * @var Producers_MixpanelEvents
122
+ */
123
+ private $_events;
124
+
125
+
126
+ /**
127
+ * An instance of the Mixpanel class (for singleton use)
128
+ * @var Mixpanel
129
+ */
130
+ private static $_instance;
131
+
132
+
133
+ /**
134
+ * Instantiates a new Mixpanel instance.
135
+ * @param $token
136
+ * @param array $options
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
+
145
+ /**
146
+ * Returns a singleton instance of Mixpanel
147
+ * @param $token
148
+ * @param array $options
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
+
159
+ /**
160
+ * Add an array representing a message to be sent to Mixpanel to the in-memory queue.
161
+ * @param array $message
162
+ */
163
+ public function enqueue($message = array()) {
164
+ $this->_events->enqueue($message);
165
+ }
166
+
167
+
168
+ /**
169
+ * Add an array representing a list of messages to be sent to Mixpanel to a queue.
170
+ * @param array $messages
171
+ */
172
+ public function enqueueAll($messages = array()) {
173
+ $this->_events->enqueueAll($messages);
174
+ }
175
+
176
+
177
+ /**
178
+ * Flush the events queue
179
+ * @param int $desired_batch_size
180
+ */
181
+ public function flush($desired_batch_size = 50) {
182
+ $this->_events->flush($desired_batch_size);
183
+ }
184
+
185
+
186
+ /**
187
+ * Empty the events queue
188
+ */
189
+ public function reset() {
190
+ $this->_events->reset();
191
+ }
192
+
193
+
194
+ /**
195
+ * Identify the user you want to associate to tracked events
196
+ * @param string|int $user_id
197
+ */
198
+ public function identify($user_id) {
199
+ $this->_events->identify($user_id);
200
+ }
201
+
202
+ /**
203
+ * Track an event defined by $event associated with metadata defined by $properties
204
+ * @param string $event
205
+ * @param array $properties
206
+ */
207
+ public function track($event, $properties = array()) {
208
+ $this->_events->track($event, $properties);
209
+ }
210
+
211
+
212
+ /**
213
+ * Register a property to be sent with every event.
214
+ *
215
+ * If the property has already been registered, it will be
216
+ * overwritten. NOTE: Registered properties are only persisted for the life of the Mixpanel class instance.
217
+ * @param string $property
218
+ * @param mixed $value
219
+ */
220
+ public function register($property, $value) {
221
+ $this->_events->register($property, $value);
222
+ }
223
+
224
+
225
+ /**
226
+ * Register multiple properties to be sent with every event.
227
+ *
228
+ * If any of the properties have already been registered,
229
+ * they will be overwritten. NOTE: Registered properties are only persisted for the life of the Mixpanel class
230
+ * instance.
231
+ * @param array $props_and_vals
232
+ */
233
+ public function registerAll($props_and_vals = array()) {
234
+ $this->_events->registerAll($props_and_vals);
235
+ }
236
+
237
+
238
+ /**
239
+ * Register a property to be sent with every event.
240
+ *
241
+ * If the property has already been registered, it will NOT be
242
+ * overwritten. NOTE: Registered properties are only persisted for the life of the Mixpanel class instance.
243
+ * @param $property
244
+ * @param $value
245
+ */
246
+ public function registerOnce($property, $value) {
247
+ $this->_events->registerOnce($property, $value);
248
+ }
249
+
250
+
251
+ /**
252
+ * Register multiple properties to be sent with every event.
253
+ *
254
+ * If any of the properties have already been registered,
255
+ * they will NOT be overwritten. NOTE: Registered properties are only persisted for the life of the Mixpanel class
256
+ * instance.
257
+ * @param array $props_and_vals
258
+ */
259
+ public function registerAllOnce($props_and_vals = array()) {
260
+ $this->_events->registerAllOnce($props_and_vals);
261
+ }
262
+
263
+
264
+ /**
265
+ * Un-register an property to be sent with every event.
266
+ * @param string $property
267
+ */
268
+ public function unregister($property) {
269
+ $this->_events->unregister($property);
270
+ }
271
+
272
+
273
+ /**
274
+ * Un-register a list of properties to be sent with every event.
275
+ * @param array $properties
276
+ */
277
+ public function unregisterAll($properties) {
278
+ $this->_events->unregisterAll($properties);
279
+ }
280
+
281
+
282
+ /**
283
+ * Get a property that is set to be sent with every event
284
+ * @param string $property
285
+ * @return mixed
286
+ */
287
+ public function getProperty($property)
288
+ {
289
+ return $this->_events->getProperty($property);
290
+ }
291
+
292
+
293
+ /**
294
+ * Alias an existing id with a different unique id. This is helpful when you want to associate a generated id
295
+ * (such as a session id) to a user id or username.
296
+ * @param string|int $original_id
297
+ * @param string|int $new_id
298
+ */
299
+ public function createAlias($original_id, $new_id) {
300
+ $this->_events->createAlias($original_id, $new_id);
301
+ }
302
+ }
lib/mixpanel/Producers/MixpanelBaseProducer.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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");
5
+ require_once(dirname(__FILE__) . "/../ConsumerStrategies/SocketConsumer.php");
6
+
7
+ if (!function_exists('json_encode')) {
8
+ throw new Exception('The JSON PHP extension is required.');
9
+ }
10
+
11
+ /**
12
+ * Provides some base methods for use by a message Producer
13
+ */
14
+ abstract class Producers_MixpanelBaseProducer extends Base_MixpanelBase {
15
+
16
+
17
+ /**
18
+ * @var string a token associated to a Mixpanel project
19
+ */
20
+ protected $_token;
21
+
22
+
23
+ /**
24
+ * @var array a queue to hold messages in memory before flushing in batches
25
+ */
26
+ private $_queue = array();
27
+
28
+
29
+ /**
30
+ * @var ConsumerStrategies_AbstractConsumer the consumer to use when flushing messages
31
+ */
32
+ private $_consumer = null;
33
+
34
+
35
+ /**
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
+
45
+ /**
46
+ * If the queue reaches this size we'll auto-flush to prevent out of memory errors
47
+ * @var int
48
+ */
49
+ protected $_max_queue_size = 1000;
50
+
51
+
52
+ /**
53
+ * Creates a new MixpanelBaseProducer, assings Mixpanel project token, registers custom Consumers, and instantiates
54
+ * the desired consumer
55
+ * @param $token
56
+ * @param array $options
57
+ */
58
+ public function __construct($token, $options = array()) {
59
+
60
+ parent::__construct($options);
61
+
62
+ // register any customer consumers
63
+ if (array_key_exists("consumers", $options)) {
64
+ $this->_consumers = array_merge($this->_consumers, $options['consumers']);
65
+ }
66
+
67
+ // set max queue size
68
+ if (array_key_exists("max_queue_size", $options)) {
69
+ $this->_max_queue_size = $options['max_queue_size'];
70
+ }
71
+
72
+ // associate token
73
+ $this->_token = $token;
74
+
75
+ if ($this->_debug()) {
76
+ $this->_log("Using token: ".$this->_token);
77
+ }
78
+
79
+ // instantiate the chosen consumer
80
+ $this->_consumer = $this->_getConsumer();
81
+
82
+ }
83
+
84
+
85
+ /**
86
+ * Flush the queue when we destruct the client with retries
87
+ */
88
+ public function __destruct() {
89
+ $attempts = 0;
90
+ $max_attempts = 10;
91
+ $success = false;
92
+ while (!$success && $attempts < $max_attempts) {
93
+ if ($this->_debug()) {
94
+ $this->_log("destruct flush attempt #".($attempts+1));
95
+ }
96
+ $success = $this->flush();
97
+ $attempts++;
98
+ }
99
+ }
100
+
101
+
102
+ /**
103
+ * Iterate the queue and write in batches using the instantiated Consumer Strategy
104
+ * @param int $desired_batch_size
105
+ * @return bool whether or not the flush was successful
106
+ */
107
+ public function flush($desired_batch_size = 50) {
108
+ $queue_size = count($this->_queue);
109
+ $succeeded = true;
110
+ if ($this->_debug()) {
111
+ $this->_log("Flush called - queue size: ".$queue_size);
112
+ }
113
+
114
+ while($queue_size > 0 && $succeeded) {
115
+ $batch_size = min(array($queue_size, $desired_batch_size, $this->_options['max_batch_size']));
116
+ $batch = array_splice($this->_queue, 0, $batch_size);
117
+ $succeeded = $this->_persist($batch);
118
+
119
+ if (!$succeeded) {
120
+ if ($this->_debug()) {
121
+ $this->_log("Batch consumption failed!");
122
+ }
123
+ $this->_queue = array_merge($batch, $this->_queue);
124
+
125
+ if ($this->_debug()) {
126
+ $this->_log("added batch back to queue, queue size is now $queue_size");
127
+ }
128
+ }
129
+
130
+ $queue_size = count($this->_queue);
131
+
132
+ if ($this->_debug()) {
133
+ $this->_log("Batch of $batch_size consumed, queue size is now $queue_size");
134
+ }
135
+ }
136
+ return $succeeded;
137
+ }
138
+
139
+
140
+ /**
141
+ * Empties the queue without persisting any of the messages
142
+ */
143
+ public function reset() {
144
+ $this->_queue = array();
145
+ }
146
+
147
+
148
+ /**
149
+ * Returns the in-memory queue
150
+ * @return array
151
+ */
152
+ public function getQueue() {
153
+ return $this->_queue;
154
+ }
155
+
156
+ /**
157
+ * Returns the current Mixpanel project token
158
+ * @return string
159
+ */
160
+ public function getToken() {
161
+ return $this->_token;
162
+ }
163
+
164
+
165
+ /**
166
+ * Given a strategy type, return a new PersistenceStrategy object
167
+ * @return ConsumerStrategies_AbstractConsumer
168
+ */
169
+ protected function _getConsumer() {
170
+ $key = $this->_options['consumer'];
171
+ $Strategy = $this->_consumers[$key];
172
+ if ($this->_debug()) {
173
+ $this->_log("Using consumer: " . $key . " -> " . $Strategy);
174
+ }
175
+ $this->_options['endpoint'] = $this->_getEndpoint();
176
+
177
+ return new $Strategy($this->_options);
178
+ }
179
+
180
+
181
+ /**
182
+ * Add an array representing a message to be sent to Mixpanel to a queue.
183
+ * @param array $message
184
+ */
185
+ public function enqueue($message = array()) {
186
+ array_push($this->_queue, $message);
187
+
188
+ // force a flush if we've reached our threshold
189
+ if (count($this->_queue) > $this->_max_queue_size) {
190
+ $this->flush();
191
+ }
192
+
193
+ if ($this->_debug()) {
194
+ $this->_log("Queued message: ".json_encode($message));
195
+ }
196
+ }
197
+
198
+
199
+ /**
200
+ * Add an array representing a list of messages to be sent to Mixpanel to a queue.
201
+ * @param array $messages
202
+ */
203
+ public function enqueueAll($messages = array()) {
204
+ foreach($messages as $message) {
205
+ $this->enqueue($message);
206
+ }
207
+
208
+ }
209
+
210
+
211
+ /**
212
+ * Given an array of messages, persist it with the instantiated Persistence Strategy
213
+ * @param $message
214
+ * @return mixed
215
+ */
216
+ protected function _persist($message) {
217
+ return $this->_consumer->persist($message);
218
+ }
219
+
220
+
221
+
222
+
223
+ /**
224
+ * Return the endpoint that should be used by a consumer that consumes messages produced by this producer.
225
+ * @return string
226
+ */
227
+ abstract function _getEndpoint();
228
+
229
+ }
lib/mixpanel/Producers/MixpanelEvents.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/MixpanelBaseProducer.php");
3
+ require_once(dirname(__FILE__) . "/MixpanelPeople.php");
4
+ require_once(dirname(__FILE__) . "/../ConsumerStrategies/CurlConsumer.php");
5
+
6
+ /**
7
+ * Provides an API to track events on Mixpanel
8
+ */
9
+ class Producers_MixpanelEvents extends Producers_MixpanelBaseProducer {
10
+
11
+ /**
12
+ * An array of properties to attach to every tracked event
13
+ * @var array
14
+ */
15
+ private $_super_properties = array("mp_lib" => "php");
16
+
17
+
18
+ /**
19
+ * Track an event defined by $event associated with metadata defined by $properties
20
+ * @param string $event
21
+ * @param array $properties
22
+ */
23
+ public function track($event, $properties = array()) {
24
+
25
+ // if no token is passed in, use current token
26
+ if (!array_key_exists("token", $properties)) $properties['token'] = $this->_token;
27
+
28
+ // if no time is passed in, use the current time
29
+ if (!array_key_exists('time', $properties)) $properties['time'] = time();
30
+
31
+ $params['event'] = $event;
32
+ $params['properties'] = array_merge($this->_super_properties, $properties);
33
+
34
+ $this->enqueue($params);
35
+ }
36
+
37
+
38
+ /**
39
+ * Register a property to be sent with every event. If the property has already been registered, it will be
40
+ * overwritten.
41
+ * @param string $property
42
+ * @param mixed $value
43
+ */
44
+ public function register($property, $value) {
45
+ $this->_super_properties[$property] = $value;
46
+ }
47
+
48
+
49
+ /**
50
+ * Register multiple properties to be sent with every event. If any of the properties have already been registered,
51
+ * they will be overwritten.
52
+ * @param array $props_and_vals
53
+ */
54
+ public function registerAll($props_and_vals = array()) {
55
+ foreach($props_and_vals as $property => $value) {
56
+ $this->register($property, $value);
57
+ }
58
+ }
59
+
60
+
61
+ /**
62
+ * Register a property to be sent with every event. If the property has already been registered, it will NOT be
63
+ * overwritten.
64
+ * @param $property
65
+ * @param $value
66
+ */
67
+ public function registerOnce($property, $value) {
68
+ if (!isset($this->_super_properties[$property])) {
69
+ $this->register($property, $value);
70
+ }
71
+ }
72
+
73
+
74
+ /**
75
+ * Register multiple properties to be sent with every event. If any of the properties have already been registered,
76
+ * they will NOT be overwritten.
77
+ * @param array $props_and_vals
78
+ */
79
+ public function registerAllOnce($props_and_vals = array()) {
80
+ foreach($props_and_vals as $property => $value) {
81
+ if (!isset($this->_super_properties[$property])) {
82
+ $this->register($property, $value);
83
+ }
84
+ }
85
+ }
86
+
87
+
88
+ /**
89
+ * Un-register an property to be sent with every event.
90
+ * @param string $property
91
+ */
92
+ public function unregister($property) {
93
+ unset($this->_super_properties[$property]);
94
+ }
95
+
96
+
97
+ /**
98
+ * Un-register a list of properties to be sent with every event.
99
+ * @param array $properties
100
+ */
101
+ public function unregisterAll($properties) {
102
+ foreach($properties as $property) {
103
+ $this->unregister($property);
104
+ }
105
+ }
106
+
107
+
108
+ /**
109
+ * Get a property that is set to be sent with every event
110
+ * @param string $property
111
+ * @return mixed
112
+ */
113
+ public function getProperty($property) {
114
+ return $this->_super_properties[$property];
115
+ }
116
+
117
+
118
+ /**
119
+ * Identify the user you want to associate to tracked events
120
+ * @param string|int $user_id
121
+ */
122
+ public function identify($user_id) {
123
+ $this->register("distinct_id", $user_id);
124
+ }
125
+
126
+
127
+ /**
128
+ * Alias an existing id with a different unique id. This is helpful when you want to associate a generated id to
129
+ * a username or e-mail address.
130
+ *
131
+ * Because aliasing can be extremely vulnerable to race conditions and ordering issues, we'll make a synchronous
132
+ * call directly to Mixpanel when this method is called. If it fails we'll throw an Exception as subsequent
133
+ * events are likely to be incorrectly tracked.
134
+ * @param string|int $original_id
135
+ * @param string|int $new_id
136
+ * @return array $msg
137
+ * @throws Exception
138
+ */
139
+ public function createAlias($original_id, $new_id) {
140
+ $msg = array(
141
+ "event" => '$create_alias',
142
+ "properties" => array("distinct_id" => $original_id, "alias" => $new_id, "token" => $this->_token)
143
+ );
144
+
145
+ $options = array_merge($this->_options, array("endpoint" => $this->_getEndpoint(), "fork" => false));
146
+ $curlConsumer = new ConsumerStrategies_CurlConsumer($options);
147
+ $success = $curlConsumer->persist(array($msg));
148
+ if (!$success) {
149
+ error_log("Creating Mixpanel Alias (original id: $original_id, new id: $new_id) failed");
150
+ throw new Exception("Tried to create an alias but the call was not successful");
151
+ } else {
152
+ return $msg;
153
+ }
154
+ }
155
+
156
+
157
+ /**
158
+ * Returns the "events" endpoint
159
+ * @return string
160
+ */
161
+ function _getEndpoint() {
162
+ return $this->_options['events_endpoint'];
163
+ }
164
+ }
lib/mixpanel/Producers/MixpanelPeople.php ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(dirname(__FILE__) . "/MixpanelBaseProducer.php");
3
+
4
+ /**
5
+ * Provides an API to create/update profiles on Mixpanel
6
+ */
7
+ class Producers_MixpanelPeople extends Producers_MixpanelBaseProducer {
8
+
9
+ /**
10
+ * Internal method to prepare a message given the message data
11
+ * @param $distinct_id
12
+ * @param $operation
13
+ * @param $value
14
+ * @param null $ip
15
+ * @return array
16
+ */
17
+ private function _constructPayload($distinct_id, $operation, $value, $ip = null) {
18
+ $payload = array(
19
+ '$token' => $this->_token,
20
+ '$distinct_id' => $distinct_id,
21
+ $operation => $value
22
+ );
23
+ if ($ip !== null) $payload['$ip'] = $ip;
24
+ return $payload;
25
+ }
26
+
27
+ /**
28
+ * Set properties on a user record. If the profile does not exist, it creates it with these properties.
29
+ * If it does exist, it sets the properties to these values, overwriting existing values.
30
+ * @param string|int $distinct_id the distinct_id or alias of a user
31
+ * @param array $props associative array of properties to set on the profile
32
+ * @param string|null $ip the ip address of the client (used for geo-location)
33
+ */
34
+ public function set($distinct_id, $props, $ip = null) {
35
+ $payload = $this->_constructPayload($distinct_id, '$set', $props, $ip);
36
+ $this->enqueue($payload);
37
+ }
38
+
39
+ /**
40
+ * Set properties on a user record. If the profile does not exist, it creates it with these properties.
41
+ * If it does exist, it sets the properties to these values but WILL NOT overwrite existing values.
42
+ * @param string|int $distinct_id the distinct_id or alias of a user
43
+ * @param array $props associative array of properties to set on the profile
44
+ * @param string|null $ip the ip address of the client (used for geo-location)
45
+ */
46
+ public function setOnce($distinct_id, $props, $ip = null) {
47
+ $payload = $this->_constructPayload($distinct_id, '$set_once', $props, $ip);
48
+ $this->enqueue($payload);
49
+ }
50
+
51
+ /**
52
+ * Unset properties on a user record. If the profile does not exist, it creates it with no properties.
53
+ * If it does exist, it unsets these properties. NOTE: In other libraries we use 'unset' which is
54
+ * a reserved word in PHP.
55
+ * @param string|int $distinct_id the distinct_id or alias of a user
56
+ * @param array $props associative array of properties to unset on the profile
57
+ * @param string|null $ip the ip address of the client (used for geo-location)
58
+ */
59
+ public function remove($distinct_id, $props, $ip = null) {
60
+ $payload = $this->_constructPayload($distinct_id, '$unset', $props, $ip);
61
+ $this->enqueue($payload);
62
+ }
63
+
64
+ /**
65
+ * Increments the value of a property on a user record. If the profile does not exist, it creates it and sets the
66
+ * property to the increment value.
67
+ * @param string|int $distinct_id the distinct_id or alias of a user
68
+ * @param $prop string the property to increment
69
+ * @param int $val the amount to increment the property by
70
+ * @param string|null $ip the ip address of the client (used for geo-location)
71
+ */
72
+ public function increment($distinct_id, $prop, $val, $ip = null) {
73
+ $payload = $this->_constructPayload($distinct_id, '$add', array("$prop" => $val), $ip);
74
+ $this->enqueue($payload);
75
+ }
76
+
77
+ /**
78
+ * Adds $val to a list located at $prop. If the property does not exist, it will be created. If $val is a string
79
+ * and the list is empty or does not exist, a new list with one value will be created.
80
+ * @param string|int $distinct_id the distinct_id or alias of a user
81
+ * @param string $prop the property that holds the list
82
+ * @param string|array $val items to add to the list
83
+ * @param string|null $ip the ip address of the client (used for geo-location)
84
+ */
85
+ public function append($distinct_id, $prop, $val, $ip = null) {
86
+ $operation = gettype($val) == "array" ? '$union' : '$append';
87
+ $payload = $this->_constructPayload($distinct_id, $operation, array("$prop" => $val), $ip);
88
+ $this->enqueue($payload);
89
+ }
90
+
91
+ /**
92
+ * Adds a transaction to the user's profile for revenue tracking
93
+ * @param string|int $distinct_id the distinct_id or alias of a user
94
+ * @param string $amount the transaction amount e.g. "20.50"
95
+ * @param null $timestamp the timestamp of when the transaction occurred (default to current timestamp)
96
+ * @param string|null $ip the ip address of the client (used for geo-location)
97
+ */
98
+ public function trackCharge($distinct_id, $amount, $timestamp = null, $ip = null) {
99
+ $timestamp = $timestamp == null ? time() : $timestamp;
100
+ $date_iso = date("c", $timestamp);
101
+ $transaction = array(
102
+ '$time' => $date_iso,
103
+ '$amount' => $amount
104
+ );
105
+ $val = array('$transactions' => $transaction);
106
+ $payload = $this->_constructPayload($distinct_id, '$append', $val, $ip);
107
+ $this->enqueue($payload);
108
+ }
109
+
110
+ /**
111
+ * Clear all transactions stored on a user's profile
112
+ * @param string|int $distinct_id the distinct_id or alias of a user
113
+ * @param string|null $ip the ip address of the client (used for geo-location)
114
+ */
115
+ public function clearCharges($distinct_id, $ip = null) {
116
+ $payload = $this->_constructPayload($distinct_id, '$set', array('$transactions' => array()), $ip);
117
+ $this->enqueue($payload);
118
+ }
119
+
120
+ /**
121
+ * Delete this profile from Mixpanel
122
+ * @param string|int $distinct_id the distinct_id or alias of a user
123
+ * @param string|null $ip the ip address of the client (used for geo-location)
124
+ */
125
+ public function deleteUser($distinct_id, $ip = null) {
126
+ $payload = $this->_constructPayload($distinct_id, '$delete', "", $ip);
127
+ $this->enqueue($payload);
128
+ }
129
+
130
+ /**
131
+ * Returns the "engage" endpoint
132
+ * @return string
133
+ */
134
+ function _getEndpoint() {
135
+ return $this->_options['people_endpoint'];
136
+ }
137
+
138
+ }
power-ups/contacts.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Power-up Name: Contacts Tracking
4
+ * Power-up Class: WPLeadInContacts
5
+ * Power-up Menu Text: Contacts
6
+ * Power-up Menu Link: contacts
7
+ * Power-up Slug: contacts
8
+ * Power-up URI: http://leadin.com/
9
+ * Power-up Description: Get an in-depth history of each contact in your database.
10
+ * Power-up Icon: powerup-icon-leads
11
+ * First Introduced: 0.4.7
12
+ * Power-up Tags: Lead Tracking
13
+ * Auto Activate: Yes
14
+ * Permanently Enabled: Yes
15
+ */
16
+
17
+ //=============================================
18
+ // Define Constants
19
+ //=============================================
20
+
21
+ if ( !defined('LEADIN_CONTACTS_PATH') )
22
+ define('LEADIN_CONTACTS_PATH', LEADIN_PATH . '/power-ups/contacts');
23
+
24
+ if ( !defined('LEADIN_CONTACTS_PLUGIN_DIR') )
25
+ define('LEADIN_CONTACTS_PLUGIN_DIR', LEADIN_PLUGIN_DIR . '/power-ups/contacts');
26
+
27
+ if ( !defined('LEADIN_CONTACTS_PLUGIN_SLUG') )
28
+ define('LEADIN_CONTACTS_PLUGIN_SLUG', basename(dirname(__FILE__)));
29
+
30
+ //=============================================
31
+ // Include Needed Files
32
+ //=============================================
33
+
34
+ require_once(LEADIN_CONTACTS_PLUGIN_DIR . '/admin/contacts-admin.php');
35
+
36
+ //=============================================
37
+ // WPLeadIn Class
38
+ //=============================================
39
+ class WPLeadInContacts extends WPLeadIn {
40
+
41
+ var $admin;
42
+
43
+ /**
44
+ * Class constructor
45
+ */
46
+ function __construct ( $activated )
47
+ {
48
+ //=============================================
49
+ // Hooks & Filters
50
+ //=============================================
51
+
52
+ if ( ! $activated )
53
+ return false;
54
+
55
+ add_action('admin_print_scripts', array(&$this, 'add_leadin_contacts_admin_scripts'));
56
+ }
57
+
58
+ public function admin_init ( )
59
+ {
60
+ $admin_class = get_class($this) . 'Admin';
61
+ $this->admin = new $admin_class();
62
+ }
63
+
64
+ function power_up_setup_callback ( )
65
+ {
66
+ $this->admin->power_up_setup_callback();
67
+ }
68
+
69
+ //=============================================
70
+ // Scripts & Styles
71
+ //=============================================
72
+
73
+ /**
74
+ * Adds admin javascript
75
+ */
76
+ function add_leadin_contacts_admin_scripts ()
77
+ {
78
+ global $pagenow;
79
+
80
+ if ( $pagenow == 'admin.php' && isset($_GET['page']) && strstr($_GET['page'], "leadin") )
81
+ {
82
+ wp_register_script('leadin-contacts-admin-js', LEADIN_CONTACTS_PATH . '/admin/js/leadin-contacts-admin.js', array ( 'jquery' ), FALSE, TRUE);
83
+ wp_enqueue_script('leadin-contacts-admin-js');
84
+ }
85
+ }
86
+ }
87
+
88
+ //=============================================
89
+ // LeadIn Init
90
+ //=============================================
91
+
92
+ global $leadin_contacts;
93
+ //$leadin_contacts = new WPLeadInContacts();
94
+
95
+ ?>
power-ups/contacts/admin/contacts-admin.php ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ //=============================================
4
+ // Include Needed Files
5
+ //=============================================
6
+
7
+
8
+ //=============================================
9
+ // WPLeadInAdmin Class
10
+ //=============================================
11
+ class WPLeadInContactsAdmin extends WPLeadInAdmin {
12
+
13
+ /**
14
+ * Class constructor
15
+ */
16
+ function __construct ()
17
+ {
18
+ //=============================================
19
+ // Hooks & Filters
20
+ //=============================================
21
+
22
+ if ( is_admin() )
23
+ {
24
+ add_action('admin_print_scripts', array(&$this, 'add_leadin_admin_scripts'));
25
+ }
26
+ }
27
+
28
+ //=============================================
29
+ // Settings Page
30
+ //=============================================
31
+
32
+ /**
33
+ * Creates settings page
34
+ */
35
+ function power_up_setup_callback ()
36
+ {
37
+ WPLeadInContactsAdmin::leadin_contacts_page();
38
+ }
39
+
40
+
41
+ //=============================================
42
+ // Contacts Page
43
+ //=============================================
44
+
45
+ /**
46
+ * Shared functionality between contact views
47
+ */
48
+ function leadin_contacts_page ()
49
+ {
50
+ global $wp_version;
51
+
52
+ $action = $this->leadin_current_action();
53
+ if ( $action == 'delete' )
54
+ {
55
+ $lead_id = ( isset($_GET['lead']) ? absint($_GET['lead']) : FALSE );
56
+ $this->delete_lead($lead_id);
57
+ }
58
+
59
+ echo '<div id="leadin" class="wrap '. ( $wp_version < 3.8 && !is_plugin_active('mp6/mp6.php') ? 'pre-mp6' : ''). '">';
60
+
61
+ if ( $action != 'view' ) {
62
+ leadin_track_plugin_activity("Loaded Contact List Page");
63
+ $this->leadin_render_list_page();
64
+ }
65
+ else {
66
+ leadin_track_plugin_activity("Loaded Contact Detail Page");
67
+ $this->leadin_render_contact_detail($_GET['lead']);
68
+ }
69
+
70
+ $this->leadin_footer();
71
+
72
+ echo '</div>';
73
+ }
74
+
75
+ /**
76
+ * GET and set url actions into readable strings
77
+ * @return string if actions are set, bool if no actions set
78
+ */
79
+ function leadin_current_action ()
80
+ {
81
+ if ( isset($_REQUEST['action']) && -1 != $_REQUEST['action'] )
82
+ return $_REQUEST['action'];
83
+
84
+ if ( isset($_REQUEST['action2']) && -1 != $_REQUEST['action2'] )
85
+ return $_REQUEST['action2'];
86
+
87
+ return FALSE;
88
+ }
89
+
90
+ /**
91
+ * Creates view a contact's deteails + timeline history
92
+ *
93
+ * @param int
94
+ */
95
+ function leadin_render_contact_detail ( $lead_id )
96
+ {
97
+ $li_contact = new LI_Contact();
98
+ $li_contact->set_hashkey_by_id($lead_id);
99
+ $li_contact->get_contact_history();
100
+
101
+ $lead_email = $li_contact->history->lead->lead_email;
102
+
103
+ echo '<a href="' . get_bloginfo('wpurl') . '/wp-admin/admin.php?page=leadin_contacts">&larr; All Contacts</a>';
104
+
105
+ echo '<div class="header-wrap">';
106
+ echo '<img height="40px" width="40px" src="https://app.getsignals.com/avatar/image/?emails=' . $lead_email . '" />';
107
+ echo '<h1 class="contact-name">' . $lead_email . '</h1>';
108
+ echo '</div>';
109
+
110
+ echo '<div id="col-container">';
111
+
112
+ echo '<div id="col-right">';
113
+ echo '<h2>Contact History</h2>';
114
+ echo '<div class="col-wrap contact-history">';
115
+ echo '<ul class="sessions">';
116
+ $sessions = array_reverse($li_contact->history->sessions);
117
+ foreach ( $sessions as &$session )
118
+ {
119
+ $first_event = array_values($session['events']);
120
+ $first_event_date = $first_event[0]['activities'][0]['event_date'];
121
+ $session_date = date('M j', strtotime($first_event_date));
122
+ $session_start_time = date('g:i a', strtotime($first_event_date));
123
+
124
+ $last_event = end($session['events']);
125
+ $last_activity = end($last_event['activities']);
126
+ $session_end_time = date('g:i a', strtotime($last_activity['event_date']));
127
+
128
+ if ( $session_end_time != $session_start_time )
129
+ $session_time_range = $session_start_time . ' - ' . $session_end_time;
130
+ else
131
+ $session_time_range = $session_start_time;
132
+
133
+ echo '<li class="session">';
134
+ echo '<h3 class="session-date">' . $session_date . '<span class="event-time-range">' . $session_time_range . '</span></h3>';
135
+
136
+ echo '<ul class="events">';
137
+
138
+ $events = $session['events'];
139
+ foreach ( $events as &$event )
140
+ {
141
+ if ( $event['event_type'] == 'pageview' )
142
+ {
143
+ $pageview = $event['activities'][0];
144
+
145
+ if ( $pageview['event_date'] == $first_event_date )
146
+ {
147
+ echo '<li class="event source">';
148
+ echo '<p class="event-title">Traffic Source: ' . ( $pageview['pageview_source'] ? '<a href="' . $pageview['pageview_source'] . '">' . $pageview['pageview_source'] : 'Direct' ) . '</a></p>';
149
+ echo '</li>';
150
+ }
151
+
152
+
153
+ echo '<li class="event pageview">';
154
+ echo '<p class="event-title">' . $pageview['pageview_title'] . '<span class="event-time-range">' . date('g:ia', strtotime($pageview['event_date'])) . '</span></p>';
155
+ echo '<a class="pageview-url" href="' . $pageview['pageview_url'] . '">' . $pageview['pageview_url'] . '</a>';
156
+ echo '</li>';
157
+ }
158
+ else if ( $event['event_type'] == 'form' )
159
+ {
160
+ $submission = $event['activities'][0];
161
+
162
+ $form_fields = json_decode(stripslashes($submission['form_fields']));
163
+ $num_form_fieds = count($form_fields);
164
+
165
+ echo '<li class="event form-submission">';
166
+ echo '<p class="event-title">Filled out form on page <a href="' . $submission['form_page_url'] . '">' . $submission['form_page_title'] . '</a><span class="event-time-range">' . date('g:ia', strtotime($submission['event_date'])) . '</span></p>';
167
+ echo '<ul class="event-detail fields">';
168
+ foreach ( $form_fields as $field )
169
+ {
170
+ echo '<li class="field">';
171
+ echo '<label class="field-label">' . $field->label . ':</label>';
172
+ echo '<p class="field-value">' . $field->value . '</p>';
173
+ echo '</li>';
174
+ }
175
+ echo '</ul>';
176
+ echo '</li>';
177
+ }
178
+ }
179
+ echo '</ul>';
180
+ echo '</li>';
181
+ }
182
+ echo '</ul>';
183
+ echo '</div>';
184
+ echo '</div>';
185
+
186
+ echo '<div id="col-left" class="metabox-holder">';
187
+ echo '<div class="col-wrap">';
188
+ echo '<div class="contact-info postbox">';
189
+ echo '<h3>Contact Information</h3>';
190
+ echo '<div class="inside">';
191
+ echo '<p><label>Email:</label> <a href="mailto:' . $lead_email . '">' . $lead_email . '</a></p>';
192
+ echo '<p><label>Status:</label> ' . $li_contact->history->lead->lead_status . '</p>';
193
+ echo '<p><label>Original referrer:</label> <a href="' . $li_contact->history->lead->lead_source . '">' . $li_contact->history->lead->lead_source . '</a></p>';
194
+ echo '<p><label>First visit:</label> ' . self::date_format_contact_stat($li_contact->history->lead->first_visit) . '</p>';
195
+ echo '<p><label>Last Visit:</label> ' . self::date_format_contact_stat($li_contact->history->lead->last_visit) . '</p>';
196
+ echo '<p><label>Total Visits:</label> ' . $li_contact->history->lead->total_visits . '</p>';
197
+ echo '<p><label>Total Pageviews:</label> ' . $li_contact->history->lead->total_pageviews . '</p>';
198
+ echo '<p><label>First submission:</label> ' . self::date_format_contact_stat($li_contact->history->lead->first_submission) . '</p>';
199
+ echo '<p><label>Last submission:</label> ' . self::date_format_contact_stat($li_contact->history->lead->last_submission) . '</p>';
200
+ echo '<p><label>Total submissions:</label> ' . $li_contact->history->lead->total_submissions . '</p>';
201
+ echo '</div>';
202
+ echo '</div>';
203
+ echo '</div>';
204
+ echo '</div>';
205
+
206
+ echo '</div>';
207
+ }
208
+
209
+
210
+ /**
211
+ * Creates list table for Contacts page
212
+ *
213
+ */
214
+ function leadin_render_list_page ()
215
+ {
216
+ global $wp_version;
217
+
218
+ //Create an instance of our package class...
219
+ $leadinListTable = new LI_List_table();
220
+
221
+ //Fetch, prepare, sort, and filter our data...
222
+ $leadinListTable->data = $leadinListTable->get_leads();
223
+ $leadinListTable->prepare_items();
224
+
225
+ ?>
226
+
227
+ <?php
228
+ $this->leadin_header('LeadIn Contacts');
229
+
230
+ $current_view = $leadinListTable->get_view();
231
+
232
+ if ( $current_view == 'lead' )
233
+ $view_label = 'Leads';
234
+ else if ( $current_view == 'comment' )
235
+ $view_label = 'Commenters';
236
+ else if ( $current_view == 'subscribe' )
237
+ $view_label = 'Subscribers';
238
+ else
239
+ $view_label = 'Contacts';
240
+ ?>
241
+
242
+ <!-- Forms are NOT created automatically, so you need to wrap the table in one to use features like bulk actions -->
243
+ <form id="leadin-contacts" method="GET">
244
+ <?php $leadinListTable->views(); ?>
245
+
246
+ <!-- For plugins, we also need to ensure that the form posts back to our current page -->
247
+ <input type="hidden" name="page" value="<?php echo $_REQUEST['page'] ?>" />
248
+
249
+ <!-- Now we can render the completed list table -->
250
+ <?php $leadinListTable->display() ?>
251
+ </form>
252
+
253
+ <form id="export-form" name="export-form" method="POST">
254
+ <p class="submit">
255
+ <?php if ( !isset($_GET['contact_type']) ) : ?>
256
+ <input type="submit" value="<?php esc_attr_e('Export All Contacts'); ?>" name="export-all" id="leadin-export-leads" class="button button-primary">
257
+ <?php endif; ?>
258
+ <input type="submit" value="<?php esc_attr_e('Export Selected ' . $view_label ); ?>" name="export-selected" id="leadin-export-selected-leads" class="button button-primary" disabled>
259
+ <input type="hidden" id="leadin-selected-contacts" name="leadin-selected-contacts" value=""/>
260
+ </p>
261
+ </form>
262
+
263
+ <?php
264
+ }
265
+
266
+ /**
267
+ * Deletes all rows from li_leads, li_pageviews and li_submissions for a given lead
268
+ *
269
+ * @param int
270
+ * @return bool
271
+ */
272
+ function delete_lead ( $lead_id )
273
+ {
274
+ global $wpdb;
275
+
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;
289
+ }
290
+
291
+
292
+ //=============================================
293
+ // Admin Styles & Scripts
294
+ //=============================================
295
+
296
+ /**
297
+ * Adds admin javascript
298
+ */
299
+ function add_leadin_admin_scripts ()
300
+ {
301
+ global $pagenow;
302
+
303
+ if ( $pagenow == 'admin.php' && isset($_GET['page']) && strstr($_GET['page'], "leadin") )
304
+ {
305
+ wp_register_script('leadin-admin-js', LEADIN_PATH . '/admin/js/leadin-admin.js', array ( 'jquery' ), FALSE, TRUE);
306
+ wp_enqueue_script('leadin-admin-js');
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Formats any timestamp to format like Feb 4 8:43pm
312
+ *
313
+ * @param string
314
+ * @return string
315
+ */
316
+ function date_format_contact_stat ( $timestamp )
317
+ {
318
+ return date('M j, Y g:i:a', strtotime($timestamp));
319
+ }
320
+
321
+ }
322
+
323
+ /** Export functionality for the contacts list */
324
+ if ( isset($_POST['export-all']) || isset($_POST['export-selected']) )
325
+ {
326
+ global $wpdb;
327
+
328
+ $sitename = sanitize_key(get_bloginfo('name'));
329
+
330
+ if ( ! empty($sitename) )
331
+ $sitename .= '.';
332
+
333
+ $filename = $sitename . '.contacts.' . date('Y-m-d-H-i-s') . '.csv';
334
+
335
+ header('Content-Description: File Transfer');
336
+ header('Content-Disposition: attachment; filename=' . $filename);
337
+ header('Content-Type: text/csv; charset=' . get_option('blog_charset'), TRUE);
338
+
339
+ $column_headers = array(
340
+ 'Email', 'Original source', 'Status', 'Visits', 'Page views', 'Forms', 'Last visit', 'Created on'
341
+ );
342
+
343
+ $fields = array(
344
+ 'lead_email', 'lead_source', 'lead_status', 'lead_visits', 'lead_pageviews', 'lead_form_submissions', 'last_visit', 'lead_date'
345
+ );
346
+
347
+ $headers = array();
348
+ foreach ( $column_headers as $key => $field )
349
+ {
350
+ $headers[] = '"' . $field . '"';
351
+ }
352
+ echo implode(',', $headers) . "\n";
353
+
354
+ $q = $wpdb->prepare("
355
+ SELECT
356
+ l.lead_id, LOWER(DATE_FORMAT(l.lead_date, %s)) AS lead_date, l.lead_ip, l.lead_source, l.lead_email, l.lead_status,
357
+ COUNT(DISTINCT s.form_id) AS lead_form_submissions,
358
+ COUNT(DISTINCT p.pageview_id) AS lead_pageviews,
359
+ (SELECT COUNT(DISTINCT p.pageview_id) FROM li_pageviews p WHERE l.hashkey = p.lead_hashkey AND p.pageview_session_start = 1) AS lead_visits,
360
+ LOWER(DATE_FORMAT(MAX(p.pageview_date), %s)) AS last_visit
361
+ FROM li_leads l
362
+ LEFT JOIN li_submissions s ON l.hashkey = s.lead_hashkey
363
+ LEFT JOIN li_pageviews p ON l.hashkey = p.lead_hashkey
364
+ WHERE l.lead_email != '' " .
365
+ ( isset ($_POST['export-selected']) ? " AND l.lead_id IN ( " . $_POST['leadin-selected-contacts'] . " ) " : "" ) .
366
+ "GROUP BY l.lead_email ORDER BY l.lead_date DESC", '%Y/%m/%d %l:%i%p', '%Y/%m/%d %l:%i%p');
367
+
368
+ $leads = $wpdb->get_results($q);
369
+
370
+ foreach ( $leads as $contacts )
371
+ {
372
+ $data = array();
373
+ foreach ( $fields as $field )
374
+ {
375
+ $value = ( isset($contacts->{$field}) ? $contacts->{$field} : '' );
376
+ $value = ( is_array($value) ? serialize($value) : $value );
377
+ $data[] = '"' . str_replace('"', '""', $value) . '"';
378
+ }
379
+ echo implode(',', $data) . "\n";
380
+ }
381
+
382
+ exit;
383
+ }
384
+
385
+ ?>
power-ups/contacts/admin/css/leadin-contacts-admin.css ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #leadin-subscribe-preview{
2
+ height: 300px;
3
+ border-radius: 5px 0 0 0;
4
+ font-family: "Helvetica Neue", sans-serif;
5
+ background: #f0f0f0;
6
+ color: #444444;
7
+ padding: 1em;
8
+ max-width: 100%;
9
+ width: 450px;
10
+ font-size: 1.1em;
11
+ line-height: 1.5em;
12
+ }
power-ups/contacts/admin/js/leadin-contacts-admin.js ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready( function ( $ ) {
2
+ $('#leadin-contacts input:checkbox').not('thead input:checkbox, tfoot input:checkbox').bind('change', function ( e ){
3
+ var cb_count = 0;
4
+ var selected_vals = '';
5
+ var $btn_selected = $('#leadin-export-selected-leads');
6
+ var $input_selected_vals = $('#leadin-selected-contacts');
7
+ var $cb_selected = $('#leadin-contacts input:checkbox:checked').not('thead input:checkbox, tfoot input:checkbox');
8
+
9
+ if ( $cb_selected.length > 0 )
10
+ {
11
+ $btn_selected.attr('disabled', false);
12
+ }
13
+ else
14
+ {
15
+ $btn_selected.attr('disabled', true);
16
+ }
17
+
18
+ $cb_selected.each( function ( e ) {
19
+ selected_vals += $(this).val();
20
+
21
+ if ( cb_count != ($cb_selected.length-1) )
22
+ selected_vals += ',';
23
+
24
+ cb_count++;
25
+ });
26
+
27
+ $input_selected_vals.val(selected_vals);
28
+ });
29
+
30
+ $('#cb-select-all-1, #cb-select-all-2').bind('change', function ( e ) {
31
+ var cb_count = 0;
32
+ var selected_vals = '';
33
+ var $this = $(this);
34
+ var $btn_selected = $('#leadin-export-selected-leads');
35
+ var $cb_selected = $('#leadin-contacts input:checkbox').not('thead input:checkbox, tfoot input:checkbox');
36
+ var $input_selected_vals = $('#leadin-selected-contacts');
37
+
38
+ $cb_selected.each( function ( e ) {
39
+ selected_vals += $(this).val();
40
+
41
+ if ( cb_count != ($cb_selected.length-1) )
42
+ selected_vals += ',';
43
+
44
+ cb_count++;
45
+ });
46
+
47
+ $input_selected_vals.val(selected_vals);
48
+
49
+ if ( !$this.is(':checked') )
50
+ {
51
+ $btn_selected.attr('disabled', true);
52
+ }
53
+ else
54
+ {
55
+ $btn_selected.attr('disabled', false);
56
+ }
57
+ });
58
+
59
+ $('.postbox .handlediv').bind('click', function ( e ) {
60
+ var $postbox = $(this).parent();
61
+
62
+ if ( $postbox.hasClass('closed') )
63
+ {
64
+ $postbox.removeClass('closed');
65
+ }
66
+ else
67
+ {
68
+ $postbox.addClass('closed');
69
+ }
70
+
71
+ });
72
+ });
power-ups/contacts/images/powerup-icon-contacts@2x.png ADDED
Binary file
power-ups/subscribe-widget.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Power-up Name: Subscribe Pop-up
4
+ * Power-up Class: WPLeadInSubscribe
5
+ * Power-up Menu Text:
6
+ * Power-up Slug: subscribe_widget
7
+ * Power-up Menu Link: settings
8
+ * Power-up URI: http://leadin.com/pop-subscribe-form-plugin-wordpress
9
+ * Power-up Description: Convert more email subscribers with our pop-up.
10
+ * Power-up Icon: powerup-icon-subscribe
11
+ * First Introduced: 0.4.7
12
+ * Power-up Tags: Lead Generation
13
+ * Auto Activate: Yes
14
+ */
15
+
16
+ //=============================================
17
+ // Define Constants
18
+ //=============================================
19
+
20
+ if ( !defined('LEADIN_SUBSCRIBE_WIDGET_PATH') )
21
+ define('LEADIN_SUBSCRIBE_WIDGET_PATH', LEADIN_PATH . '/power-ups/subscribe-widget');
22
+
23
+ if ( !defined('LEADIN_SUBSCRIBE_WIDGET_PLUGIN_DIR') )
24
+ define('LEADIN_SUBSCRIBE_WIDGET_PLUGIN_DIR', LEADIN_PLUGIN_DIR . '/power-ups/subscribe-widget');
25
+
26
+ if ( !defined('LEADIN_SUBSCRIBE_WIDGET_PLUGIN_SLUG') )
27
+ define('LEADIN_SUBSCRIBE_PLUGIN_SLUG', basename(dirname(__FILE__)));
28
+
29
+ //=============================================
30
+ // Include Needed Files
31
+ //=============================================
32
+ require_once(LEADIN_SUBSCRIBE_WIDGET_PLUGIN_DIR . '/admin/subscribe-widget-admin.php');
33
+
34
+ //=============================================
35
+ // WPLeadIn Class
36
+ //=============================================
37
+ class WPLeadInSubscribe extends WPLeadIn {
38
+
39
+ var $admin;
40
+
41
+ /**
42
+ * Class constructor
43
+ */
44
+ function __construct ( $activated )
45
+ {
46
+ //=============================================
47
+ // Hooks & Filters
48
+ //=============================================
49
+
50
+ if ( ! $activated )
51
+ return false;
52
+
53
+ add_filter('init', array($this, 'add_leadin_subscribe_frontend_scripts_and_styles'));
54
+
55
+ add_action('get_footer', array(&$this, 'append_leadin_subscribe_heading'));
56
+ }
57
+
58
+ public function admin_init ( )
59
+ {
60
+ $admin_class = get_class($this) . 'Admin';
61
+ $this->admin = new $admin_class();
62
+ }
63
+
64
+ function power_up_setup_callback ( )
65
+ {
66
+ $this->admin->power_up_setup_callback();
67
+ }
68
+
69
+ /**
70
+ * Activate the power-up
71
+ */
72
+ function add_leadin_subscribe_defaults ()
73
+ {
74
+ $lis_options = get_option('leadin_subscribe_options');
75
+
76
+ if ( ($lis_options['li_susbscibe_installed'] != 1) || (!is_array($lis_options)) )
77
+ {
78
+ $opt = array(
79
+ 'li_susbscibe_installed' => '1',
80
+ 'li_subscribe_heading' => 'Sign up for my newsletter to get new posts by email'
81
+ );
82
+
83
+ update_option('leadin_subscribe_options', $opt);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Adds a hidden input at the end of the content containing the ouput of the heading options
89
+ *
90
+ * @return
91
+ */
92
+ function append_leadin_subscribe_heading ()
93
+ {
94
+ $lis_options = get_option('leadin_subscribe_options');
95
+
96
+ // Heading for the subscribe plugin
97
+ 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"/>';
98
+
99
+ // Div checked by media query for mobile
100
+ echo '<span id="leadin-subscribe-mobile-check"></span>';
101
+ }
102
+
103
+ //=============================================
104
+ // Scripts & Styles
105
+ //=============================================
106
+
107
+ /**
108
+ * Adds front end javascript + initializes ajax object
109
+ */
110
+ function add_leadin_subscribe_frontend_scripts_and_styles ()
111
+ {
112
+ global $pagenow;
113
+
114
+ if ( !is_admin() && $pagenow != 'wp-login.php' )
115
+ {
116
+ wp_register_script('leadin-subscribe', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/js/leadin-subscribe.js', array ('jquery', 'leadin'), false, true);
117
+ wp_register_script('vex', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/js/vex.js', array ('jquery', 'leadin'), false, true);
118
+ wp_register_script('vex-dialog', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/js/vex.dialog.js', array ('jquery', 'leadin'), false, true);
119
+
120
+ wp_enqueue_script('leadin-subscribe');
121
+ wp_enqueue_script('vex');
122
+ wp_enqueue_script('vex-dialog');
123
+
124
+ wp_register_style('leadin-subscribe-css', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/css/leadin-subscribe.css');
125
+ wp_register_style('leadin-subscribe-vex-css', LEADIN_SUBSCRIBE_WIDGET_PATH . '/frontend/css/vex.css');
126
+
127
+ wp_enqueue_style('leadin-subscribe-vex-css');
128
+ wp_enqueue_style('leadin-subscribe-css');
129
+
130
+
131
+ //wp_localize_script('leadin', 'li_ajax', array('ajax_url' => admin_url('admin-ajax.php')));
132
+ }
133
+ }
134
+
135
+ function notify_new_post($post_id) {
136
+ if( ( $_POST['post_status'] == 'publish' ) && ( $_POST['original_post_status'] != 'publish' ) ) {
137
+ $headers = "From: LeadIn <team@leadin.com>\r\n";
138
+ $headers.= "Reply-To: LeadIn <team@leadin.com>\r\n";
139
+ $headers.= "X-Mailer: PHP/" . phpversion()."\r\n";
140
+ $headers.= "MIME-Version: 1.0\r\n";
141
+ $headers.= "Content-type: text/html; charset=utf-8\r\n";
142
+
143
+ $post = get_post($post_id);
144
+ $author = get_userdata($post->post_author);
145
+ $author_email = $author->user_email;
146
+ $email_subject = "Your post has been published.";
147
+
148
+ ob_start(); ?>
149
+
150
+ <html>
151
+ <head>
152
+ <title>New post at <?php bloginfo( 'name' ) ?></title>
153
+ </head>
154
+ <body>
155
+ <p>
156
+ Hi <?php echo $author->user_firstname ?>,
157
+ </p>
158
+ <p>
159
+ Your post <a href="<?php echo get_permalink($post->ID) ?>"><?php the_title_attribute() ?></a> has been published.
160
+ </p>
161
+ </body>
162
+ </html>
163
+
164
+ <?php
165
+
166
+ $message = ob_get_contents();
167
+
168
+ ob_end_clean();
169
+
170
+ wp_mail( 'andy@leadin.com', $email_subject, $message );
171
+ }
172
+ }
173
+ }
174
+
175
+ //=============================================
176
+ // Subscribe Widget Init
177
+ //=============================================
178
+
179
+ global $leadin_subscribe_wp;
180
+ //$leadin_subscribe_wp = new WPLeadInSubscribe();
181
+
182
+ ?>
power-ups/subscribe-widget/admin/subscribe-widget-admin.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ //=============================================
4
+ // Include Needed Files
5
+ //=============================================
6
+
7
+
8
+ //=============================================
9
+ // WPLeadInAdmin Class
10
+ //=============================================
11
+ class WPLeadInSubscribeAdmin extends WPLeadInAdmin {
12
+
13
+ var $power_up_settings_section = 'leadin_subscribe_options_section';
14
+
15
+ /**
16
+ * Class constructor
17
+ */
18
+ function __construct ()
19
+ {
20
+ //=============================================
21
+ // Hooks & Filters
22
+ //=============================================
23
+
24
+ if ( is_admin() )
25
+ {
26
+ add_action('admin_init', array($this, 'leadin_subscribe_build_settings_page'));
27
+ add_action('admin_print_scripts', array($this, 'add_leadin_subscribe_admin_scripts'));
28
+ add_action('admin_print_styles', array($this, 'add_leadin_subscribe_admin_styles'));
29
+ }
30
+ }
31
+
32
+ //=============================================
33
+ // Settings Page
34
+ //=============================================
35
+
36
+ /**
37
+ * Creates settings options
38
+ */
39
+ function leadin_subscribe_build_settings_page ()
40
+ {
41
+ register_setting('leadin_settings_options', 'leadin_subscribe_options', array($this, 'sanitize'));
42
+ add_settings_section($this->power_up_settings_section, 'Subscribe Pop-in', '', LEADIN_ADMIN_PATH);
43
+ add_settings_field('li_subscribe_heading', 'Call-to-action text', array($this, 'li_subscribe_heading_callback'), LEADIN_ADMIN_PATH, $this->power_up_settings_section);
44
+ }
45
+
46
+ /**
47
+ * Sanitize each setting field as needed
48
+ *
49
+ * @param array $input Contains all settings fields as array keys
50
+ */
51
+ public function sanitize ( $input )
52
+ {
53
+ $new_input = array();
54
+
55
+ if( isset( $input['li_subscribe_heading'] ) )
56
+ $new_input['li_subscribe_heading'] = sanitize_text_field( $input['li_subscribe_heading'] );
57
+
58
+ return $new_input;
59
+ }
60
+
61
+ /**
62
+ * Prints email input for settings page
63
+ */
64
+ function li_subscribe_heading_callback ()
65
+ {
66
+ $options = get_option('leadin_subscribe_options');
67
+ $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
68
+
69
+ printf(
70
+ '<input id="li_subscribe_heading" type="text" id="title" name="leadin_subscribe_options[li_subscribe_heading]" value="%s" size="50"/>',
71
+ $li_subscribe_heading
72
+ );
73
+
74
+ }
75
+
76
+ //=============================================
77
+ // Admin Styles & Scripts
78
+ //=============================================
79
+
80
+ /**
81
+ * Adds admin javascript
82
+ */
83
+ function add_leadin_subscribe_admin_scripts ()
84
+ {
85
+ global $pagenow;
86
+
87
+ if ( $pagenow == 'admin.php' && isset($_GET['page']) && strstr($_GET['page'], "leadin_settings") )
88
+ {
89
+ wp_register_script('leadin-subscribe-admin-js', LEADIN_SUBSCRIBE_WIDGET_PATH . '/admin/js/leadin-subscribe-admin.js', array ( 'jquery' ), FALSE, TRUE);
90
+ wp_enqueue_script('leadin-subscribe-admin-js');
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Adds admin javascript
96
+ */
97
+ function add_leadin_subscribe_admin_styles ()
98
+ {
99
+ global $pagenow;
100
+
101
+ if ( $pagenow == 'admin.php' && isset($_GET['page']) && strstr($_GET['page'], "leadin_settings") )
102
+ {
103
+ wp_register_style('leadin-subscribe-admin-css', LEADIN_SUBSCRIBE_WIDGET_PATH . '/admin/css/leadin-subscribe-admin.css');
104
+ wp_enqueue_style('leadin-subscribe-admin-css');
105
+ }
106
+ }
107
+ }
108
+
109
+ ?>
power-ups/subscribe-widget/frontend/css/leadin-subscribe.css ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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/css/vex.css ADDED
@@ -0,0 +1,976 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @keyframes vex-fadein {
2
+ /* line 9, ../sass/_keyframes.sass */
3
+ 0% {
4
+ opacity: 0;
5
+ }
6
+
7
+ /* line 11, ../sass/_keyframes.sass */
8
+ 100% {
9
+ opacity: 1;
10
+ }
11
+ }
12
+
13
+ @-webkit-keyframes vex-fadein {
14
+ /* line 9, ../sass/_keyframes.sass */
15
+ 0% {
16
+ opacity: 0;
17
+ }
18
+
19
+ /* line 11, ../sass/_keyframes.sass */
20
+ 100% {
21
+ opacity: 1;
22
+ }
23
+ }
24
+
25
+ @-moz-keyframes vex-fadein {
26
+ /* line 9, ../sass/_keyframes.sass */
27
+ 0% {
28
+ opacity: 0;
29
+ }
30
+
31
+ /* line 11, ../sass/_keyframes.sass */
32
+ 100% {
33
+ opacity: 1;
34
+ }
35
+ }
36
+
37
+ @-ms-keyframes vex-fadein {
38
+ /* line 9, ../sass/_keyframes.sass */
39
+ 0% {
40
+ opacity: 0;
41
+ }
42
+
43
+ /* line 11, ../sass/_keyframes.sass */
44
+ 100% {
45
+ opacity: 1;
46
+ }
47
+ }
48
+
49
+ @-o-keyframes vex-fadein {
50
+ /* line 9, ../sass/_keyframes.sass */
51
+ 0% {
52
+ opacity: 0;
53
+ }
54
+
55
+ /* line 11, ../sass/_keyframes.sass */
56
+ 100% {
57
+ opacity: 1;
58
+ }
59
+ }
60
+
61
+ @keyframes vex-fadeout {
62
+ /* line 16, ../sass/_keyframes.sass */
63
+ 0% {
64
+ opacity: 1;
65
+ }
66
+
67
+ /* line 18, ../sass/_keyframes.sass */
68
+ 100% {
69
+ opacity: 0;
70
+ }
71
+ }
72
+
73
+ @-webkit-keyframes vex-fadeout {
74
+ /* line 16, ../sass/_keyframes.sass */
75
+ 0% {
76
+ opacity: 1;
77
+ }
78
+
79
+ /* line 18, ../sass/_keyframes.sass */
80
+ 100% {
81
+ opacity: 0;
82
+ }
83
+ }
84
+
85
+ @-moz-keyframes vex-fadeout {
86
+ /* line 16, ../sass/_keyframes.sass */
87
+ 0% {
88
+ opacity: 1;
89
+ }
90
+
91
+ /* line 18, ../sass/_keyframes.sass */
92
+ 100% {
93
+ opacity: 0;
94
+ }
95
+ }
96
+
97
+ @-ms-keyframes vex-fadeout {
98
+ /* line 16, ../sass/_keyframes.sass */
99
+ 0% {
100
+ opacity: 1;
101
+ }
102
+
103
+ /* line 18, ../sass/_keyframes.sass */
104
+ 100% {
105
+ opacity: 0;
106
+ }
107
+ }
108
+
109
+ @-o-keyframes vex-fadeout {
110
+ /* line 16, ../sass/_keyframes.sass */
111
+ 0% {
112
+ opacity: 1;
113
+ }
114
+
115
+ /* line 18, ../sass/_keyframes.sass */
116
+ 100% {
117
+ opacity: 0;
118
+ }
119
+ }
120
+
121
+ @keyframes vex-rotation {
122
+ /* line 127, ../sass/_keyframes.sass */
123
+ 0% {
124
+ transform: rotate(0deg);
125
+ -webkit-transform: rotate(0deg);
126
+ -moz-transform: rotate(0deg);
127
+ -ms-transform: rotate(0deg);
128
+ -o-transform: rotate(0deg);
129
+ }
130
+
131
+ /* line 129, ../sass/_keyframes.sass */
132
+ 100% {
133
+ transform: rotate(359deg);
134
+ -webkit-transform: rotate(359deg);
135
+ -moz-transform: rotate(359deg);
136
+ -ms-transform: rotate(359deg);
137
+ -o-transform: rotate(359deg);
138
+ }
139
+ }
140
+
141
+ @-webkit-keyframes vex-rotation {
142
+ /* line 127, ../sass/_keyframes.sass */
143
+ 0% {
144
+ transform: rotate(0deg);
145
+ -webkit-transform: rotate(0deg);
146
+ -moz-transform: rotate(0deg);
147
+ -ms-transform: rotate(0deg);
148
+ -o-transform: rotate(0deg);
149
+ }
150
+
151
+ /* line 129, ../sass/_keyframes.sass */
152
+ 100% {
153
+ transform: rotate(359deg);
154
+ -webkit-transform: rotate(359deg);
155
+ -moz-transform: rotate(359deg);
156
+ -ms-transform: rotate(359deg);
157
+ -o-transform: rotate(359deg);
158
+ }
159
+ }
160
+
161
+ @-moz-keyframes vex-rotation {
162
+ /* line 127, ../sass/_keyframes.sass */
163
+ 0% {
164
+ transform: rotate(0deg);
165
+ -webkit-transform: rotate(0deg);
166
+ -moz-transform: rotate(0deg);
167
+ -ms-transform: rotate(0deg);
168
+ -o-transform: rotate(0deg);
169
+ }
170
+
171
+ /* line 129, ../sass/_keyframes.sass */
172
+ 100% {
173
+ transform: rotate(359deg);
174
+ -webkit-transform: rotate(359deg);
175
+ -moz-transform: rotate(359deg);
176
+ -ms-transform: rotate(359deg);
177
+ -o-transform: rotate(359deg);
178
+ }
179
+ }
180
+
181
+ @-ms-keyframes vex-rotation {
182
+ /* line 127, ../sass/_keyframes.sass */
183
+ 0% {
184
+ transform: rotate(0deg);
185
+ -webkit-transform: rotate(0deg);
186
+ -moz-transform: rotate(0deg);
187
+ -ms-transform: rotate(0deg);
188
+ -o-transform: rotate(0deg);
189
+ }
190
+
191
+ /* line 129, ../sass/_keyframes.sass */
192
+ 100% {
193
+ transform: rotate(359deg);
194
+ -webkit-transform: rotate(359deg);
195
+ -moz-transform: rotate(359deg);
196
+ -ms-transform: rotate(359deg);
197
+ -o-transform: rotate(359deg);
198
+ }
199
+ }
200
+
201
+ @-o-keyframes vex-rotation {
202
+ /* line 127, ../sass/_keyframes.sass */
203
+ 0% {
204
+ transform: rotate(0deg);
205
+ -webkit-transform: rotate(0deg);
206
+ -moz-transform: rotate(0deg);
207
+ -ms-transform: rotate(0deg);
208
+ -o-transform: rotate(0deg);
209
+ }
210
+
211
+ /* line 129, ../sass/_keyframes.sass */
212
+ 100% {
213
+ transform: rotate(359deg);
214
+ -webkit-transform: rotate(359deg);
215
+ -moz-transform: rotate(359deg);
216
+ -ms-transform: rotate(359deg);
217
+ -o-transform: rotate(359deg);
218
+ }
219
+ }
220
+
221
+ /* line 11, ../sass/vex.sass */
222
+ .vex, .vex *, .vex *:before, .vex *:after {
223
+ -webkit-box-sizing: border-box;
224
+ -moz-box-sizing: border-box;
225
+ box-sizing: border-box;
226
+ }
227
+
228
+ /* line 14, ../sass/vex.sass */
229
+ .vex {
230
+ position: fixed;
231
+ overflow: auto;
232
+ -webkit-overflow-scrolling: touch;
233
+ z-index: 1111;
234
+ top: 0;
235
+ right: 0;
236
+ bottom: 0;
237
+ left: 0;
238
+ }
239
+
240
+ /* line 25, ../sass/vex.sass */
241
+ .vex-overlay {
242
+ background: black;
243
+ filter: alpha(opacity=40);
244
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
245
+ }
246
+
247
+ /* line 30, ../sass/vex.sass */
248
+ .vex-overlay {
249
+ animation: vex-fadein 0.5s;
250
+ -webkit-animation: vex-fadein 0.5s;
251
+ -moz-animation: vex-fadein 0.5s;
252
+ -ms-animation: vex-fadein 0.5s;
253
+ -o-animation: vex-fadein 0.5s;
254
+ -webkit-backface-visibility: hidden;
255
+ position: fixed;
256
+ background: rgba(0, 0, 0, 0.4);
257
+ top: 0;
258
+ right: 0;
259
+ bottom: 0;
260
+ left: 0;
261
+ }
262
+ /* line 39, ../sass/vex.sass */
263
+ .vex.vex-closing .vex-overlay {
264
+ animation: vex-fadeout 0.5s;
265
+ -webkit-animation: vex-fadeout 0.5s;
266
+ -moz-animation: vex-fadeout 0.5s;
267
+ -ms-animation: vex-fadeout 0.5s;
268
+ -o-animation: vex-fadeout 0.5s;
269
+ -webkit-backface-visibility: hidden;
270
+ }
271
+
272
+ /* line 42, ../sass/vex.sass */
273
+ .vex-content {
274
+ animation: vex-fadein 0.5s;
275
+ -webkit-animation: vex-fadein 0.5s;
276
+ -moz-animation: vex-fadein 0.5s;
277
+ -ms-animation: vex-fadein 0.5s;
278
+ -o-animation: vex-fadein 0.5s;
279
+ -webkit-backface-visibility: hidden;
280
+ background: white;
281
+ }
282
+ /* line 46, ../sass/vex.sass */
283
+ .vex.vex-closing .vex-content {
284
+ animation: vex-fadeout 0.5s;
285
+ -webkit-animation: vex-fadeout 0.5s;
286
+ -moz-animation: vex-fadeout 0.5s;
287
+ -ms-animation: vex-fadeout 0.5s;
288
+ -o-animation: vex-fadeout 0.5s;
289
+ -webkit-backface-visibility: hidden;
290
+ }
291
+
292
+ /* line 49, ../sass/vex.sass */
293
+ .vex-close:before {
294
+ font-family: Arial, sans-serif;
295
+ content: "\00D7";
296
+ }
297
+
298
+ /* line 53, ../sass/vex.sass */
299
+ .vex-dialog-form {
300
+ margin: 0;
301
+ }
302
+
303
+ /* line 56, ../sass/vex.sass */
304
+ .vex-dialog-button {
305
+ -webkit-appearance: none;
306
+ cursor: pointer;
307
+ }
308
+
309
+ /* line 60, ../sass/vex.sass */
310
+ .vex-loading-spinner {
311
+ animation: vex-rotation 0.7s linear infinite;
312
+ -webkit-animation: vex-rotation 0.7s linear infinite;
313
+ -moz-animation: vex-rotation 0.7s linear infinite;
314
+ -ms-animation: vex-rotation 0.7s linear infinite;
315
+ -o-animation: vex-rotation 0.7s linear infinite;
316
+ -webkit-backface-visibility: hidden;
317
+ -webkit-box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
318
+ -moz-box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
319
+ box-shadow: 0 0 1em rgba(0, 0, 0, 0.1);
320
+ position: fixed;
321
+ z-index: 1112;
322
+ margin: auto;
323
+ top: 0;
324
+ right: 0;
325
+ bottom: 0;
326
+ left: 0;
327
+ height: 2em;
328
+ width: 2em;
329
+ background: white;
330
+ }
331
+
332
+ /* line 76, ../sass/vex.sass */
333
+ body.vex-open {
334
+ overflow: hidden;
335
+ }
336
+
337
+ @keyframes vex-slideup {
338
+ /* line 83, ../sass/_keyframes.sass */
339
+ 0% {
340
+ transform: translateY(0);
341
+ -webkit-transform: translateY(0);
342
+ -moz-transform: translateY(0);
343
+ -ms-transform: translateY(0);
344
+ -o-transform: translateY(0);
345
+ opacity: 0;
346
+ }
347
+
348
+ /* line 86, ../sass/_keyframes.sass */
349
+ 1% {
350
+ transform: translateY(800px);
351
+ -webkit-transform: translateY(800px);
352
+ -moz-transform: translateY(800px);
353
+ -ms-transform: translateY(800px);
354
+ -o-transform: translateY(800px);
355
+ opacity: 0;
356
+ }
357
+
358
+ /* line 91, ../sass/_keyframes.sass */
359
+ 2% {
360
+ transform: translateY(800px);
361
+ -webkit-transform: translateY(800px);
362
+ -moz-transform: translateY(800px);
363
+ -ms-transform: translateY(800px);
364
+ -o-transform: translateY(800px);
365
+ opacity: 1;
366
+ }
367
+
368
+ /* line 94, ../sass/_keyframes.sass */
369
+ 100% {
370
+ transform: translateY(0);
371
+ -webkit-transform: translateY(0);
372
+ -moz-transform: translateY(0);
373
+ -ms-transform: translateY(0);
374
+ -o-transform: translateY(0);
375
+ opacity: 1;
376
+ }
377
+ }
378
+
379
+ @-webkit-keyframes vex-slideup {
380
+ /* line 83, ../sass/_keyframes.sass */
381
+ 0% {
382
+ transform: translateY(0);
383
+ -webkit-transform: translateY(0);
384
+ -moz-transform: translateY(0);
385
+ -ms-transform: translateY(0);
386
+ -o-transform: translateY(0);
387
+ opacity: 0;
388
+ }
389
+
390
+ /* line 86, ../sass/_keyframes.sass */
391
+ 1% {
392
+ transform: translateY(800px);
393
+ -webkit-transform: translateY(800px);
394
+ -moz-transform: translateY(800px);
395
+ -ms-transform: translateY(800px);
396
+ -o-transform: translateY(800px);
397
+ opacity: 0;
398
+ }
399
+
400
+ /* line 91, ../sass/_keyframes.sass */
401
+ 2% {
402
+ transform: translateY(800px);
403
+ -webkit-transform: translateY(800px);
404
+ -moz-transform: translateY(800px);
405
+ -ms-transform: translateY(800px);
406
+ -o-transform: translateY(800px);
407
+ opacity: 1;
408
+ }
409
+
410
+ /* line 94, ../sass/_keyframes.sass */
411
+ 100% {
412
+ transform: translateY(0);
413
+ -webkit-transform: translateY(0);
414
+ -moz-transform: translateY(0);
415
+ -ms-transform: translateY(0);
416
+ -o-transform: translateY(0);
417
+ opacity: 1;
418
+ }
419
+ }
420
+
421
+ @-moz-keyframes vex-slideup {
422
+ /* line 83, ../sass/_keyframes.sass */
423
+ 0% {
424
+ transform: translateY(0);
425
+ -webkit-transform: translateY(0);
426
+ -moz-transform: translateY(0);
427
+ -ms-transform: translateY(0);
428
+ -o-transform: translateY(0);
429
+ opacity: 0;
430
+ }
431
+
432
+ /* line 86, ../sass/_keyframes.sass */
433
+ 1% {
434
+ transform: translateY(800px);
435
+ -webkit-transform: translateY(800px);
436
+ -moz-transform: translateY(800px);
437
+ -ms-transform: translateY(800px);
438
+ -o-transform: translateY(800px);
439
+ opacity: 0;
440
+ }
441
+
442
+ /* line 91, ../sass/_keyframes.sass */
443
+ 2% {
444
+ transform: translateY(800px);
445
+ -webkit-transform: translateY(800px);
446
+ -moz-transform: translateY(800px);
447
+ -ms-transform: translateY(800px);
448
+ -o-transform: translateY(800px);
449
+ opacity: 1;
450
+ }
451
+
452
+ /* line 94, ../sass/_keyframes.sass */
453
+ 100% {
454
+ transform: translateY(0);
455
+ -webkit-transform: translateY(0);
456
+ -moz-transform: translateY(0);
457
+ -ms-transform: translateY(0);
458
+ -o-transform: translateY(0);
459
+ opacity: 1;
460
+ }
461
+ }
462
+
463
+ @-ms-keyframes vex-slideup {
464
+ /* line 83, ../sass/_keyframes.sass */
465
+ 0% {
466
+ transform: translateY(0);
467
+ -webkit-transform: translateY(0);
468
+ -moz-transform: translateY(0);
469
+ -ms-transform: translateY(0);
470
+ -o-transform: translateY(0);
471
+ opacity: 0;
472
+ }
473
+
474
+ /* line 86, ../sass/_keyframes.sass */
475
+ 1% {
476
+ transform: translateY(800px);
477
+ -webkit-transform: translateY(800px);
478
+ -moz-transform: translateY(800px);
479
+ -ms-transform: translateY(800px);
480
+ -o-transform: translateY(800px);
481
+ opacity: 0;
482
+ }
483
+
484
+ /* line 91, ../sass/_keyframes.sass */
485
+ 2% {
486
+ transform: translateY(800px);
487
+ -webkit-transform: translateY(800px);
488
+ -moz-transform: translateY(800px);
489
+ -ms-transform: translateY(800px);
490
+ -o-transform: translateY(800px);
491
+ opacity: 1;
492
+ }
493
+
494
+ /* line 94, ../sass/_keyframes.sass */
495
+ 100% {
496
+ transform: translateY(0);
497
+ -webkit-transform: translateY(0);
498
+ -moz-transform: translateY(0);
499
+ -ms-transform: translateY(0);
500
+ -o-transform: translateY(0);
501
+ opacity: 1;
502
+ }
503
+ }
504
+
505
+ @-o-keyframes vex-slideup {
506
+ /* line 83, ../sass/_keyframes.sass */
507
+ 0% {
508
+ transform: translateY(0);
509
+ -webkit-transform: translateY(0);
510
+ -moz-transform: translateY(0);
511
+ -ms-transform: translateY(0);
512
+ -o-transform: translateY(0);
513
+ opacity: 0;
514
+ }
515
+
516
+ /* line 86, ../sass/_keyframes.sass */
517
+ 1% {
518
+ transform: translateY(800px);
519
+ -webkit-transform: translateY(800px);
520
+ -moz-transform: translateY(800px);
521
+ -ms-transform: translateY(800px);
522
+ -o-transform: translateY(800px);
523
+ opacity: 0;
524
+ }
525
+
526
+ /* line 91, ../sass/_keyframes.sass */
527
+ 2% {
528
+ transform: translateY(800px);
529
+ -webkit-transform: translateY(800px);
530
+ -moz-transform: translateY(800px);
531
+ -ms-transform: translateY(800px);
532
+ -o-transform: translateY(800px);
533
+ opacity: 1;
534
+ }
535
+
536
+ /* line 94, ../sass/_keyframes.sass */
537
+ 100% {
538
+ transform: translateY(0);
539
+ -webkit-transform: translateY(0);
540
+ -moz-transform: translateY(0);
541
+ -ms-transform: translateY(0);
542
+ -o-transform: translateY(0);
543
+ opacity: 1;
544
+ }
545
+ }
546
+
547
+ @keyframes vex-slidedown {
548
+ /* line 100, ../sass/_keyframes.sass */
549
+ 0% {
550
+ transform: translateY(0);
551
+ -webkit-transform: translateY(0);
552
+ -moz-transform: translateY(0);
553
+ -ms-transform: translateY(0);
554
+ -o-transform: translateY(0);
555
+ }
556
+
557
+ /* line 102, ../sass/_keyframes.sass */
558
+ 100% {
559
+ transform: translateY(800px);
560
+ -webkit-transform: translateY(800px);
561
+ -moz-transform: translateY(800px);
562
+ -ms-transform: translateY(800px);
563
+ -o-transform: translateY(800px);
564
+ }
565
+ }
566
+
567
+ @-webkit-keyframes vex-slidedown {
568
+ /* line 100, ../sass/_keyframes.sass */
569
+ 0% {
570
+ transform: translateY(0);
571
+ -webkit-transform: translateY(0);
572
+ -moz-transform: translateY(0);
573
+ -ms-transform: translateY(0);
574
+ -o-transform: translateY(0);
575
+ }
576
+
577
+ /* line 102, ../sass/_keyframes.sass */
578
+ 100% {
579
+ transform: translateY(800px);
580
+ -webkit-transform: translateY(800px);
581
+ -moz-transform: translateY(800px);
582
+ -ms-transform: translateY(800px);
583
+ -o-transform: translateY(800px);
584
+ }
585
+ }
586
+
587
+ @-moz-keyframes vex-slidedown {
588
+ /* line 100, ../sass/_keyframes.sass */
589
+ 0% {
590
+ transform: translateY(0);
591
+ -webkit-transform: translateY(0);
592
+ -moz-transform: translateY(0);
593
+ -ms-transform: translateY(0);
594
+ -o-transform: translateY(0);
595
+ }
596
+
597
+ /* line 102, ../sass/_keyframes.sass */
598
+ 100% {
599
+ transform: translateY(800px);
600
+ -webkit-transform: translateY(800px);
601
+ -moz-transform: translateY(800px);
602
+ -ms-transform: translateY(800px);
603
+ -o-transform: translateY(800px);
604
+ }
605
+ }
606
+
607
+ @-ms-keyframes vex-slidedown {
608
+ /* line 100, ../sass/_keyframes.sass */
609
+ 0% {
610
+ transform: translateY(0);
611
+ -webkit-transform: translateY(0);
612
+ -moz-transform: translateY(0);
613
+ -ms-transform: translateY(0);
614
+ -o-transform: translateY(0);
615
+ }
616
+
617
+ /* line 102, ../sass/_keyframes.sass */
618
+ 100% {
619
+ transform: translateY(800px);
620
+ -webkit-transform: translateY(800px);
621
+ -moz-transform: translateY(800px);
622
+ -ms-transform: translateY(800px);
623
+ -o-transform: translateY(800px);
624
+ }
625
+ }
626
+
627
+ @-o-keyframes vex-slidedown {
628
+ /* line 100, ../sass/_keyframes.sass */
629
+ 0% {
630
+ transform: translateY(0);
631
+ -webkit-transform: translateY(0);
632
+ -moz-transform: translateY(0);
633
+ -ms-transform: translateY(0);
634
+ -o-transform: translateY(0);
635
+ }
636
+
637
+ /* line 102, ../sass/_keyframes.sass */
638
+ 100% {
639
+ transform: translateY(800px);
640
+ -webkit-transform: translateY(800px);
641
+ -moz-transform: translateY(800px);
642
+ -ms-transform: translateY(800px);
643
+ -o-transform: translateY(800px);
644
+ }
645
+ }
646
+
647
+ @keyframes vex-pulse {
648
+ /* line 136, ../sass/_keyframes.sass */
649
+ 0% {
650
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
651
+ -moz-box-shadow: inset 0 0 0 300px transparent;
652
+ box-shadow: inset 0 0 0 300px transparent;
653
+ }
654
+
655
+ /* line 138, ../sass/_keyframes.sass */
656
+ 70% {
657
+ -webkit-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
658
+ -moz-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
659
+ box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
660
+ }
661
+
662
+ /* line 140, ../sass/_keyframes.sass */
663
+ 100% {
664
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
665
+ -moz-box-shadow: inset 0 0 0 300px transparent;
666
+ box-shadow: inset 0 0 0 300px transparent;
667
+ }
668
+ }
669
+
670
+ @-webkit-keyframes vex-pulse {
671
+ /* line 136, ../sass/_keyframes.sass */
672
+ 0% {
673
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
674
+ -moz-box-shadow: inset 0 0 0 300px transparent;
675
+ box-shadow: inset 0 0 0 300px transparent;
676
+ }
677
+
678
+ /* line 138, ../sass/_keyframes.sass */
679
+ 70% {
680
+ -webkit-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
681
+ -moz-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
682
+ box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
683
+ }
684
+
685
+ /* line 140, ../sass/_keyframes.sass */
686
+ 100% {
687
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
688
+ -moz-box-shadow: inset 0 0 0 300px transparent;
689
+ box-shadow: inset 0 0 0 300px transparent;
690
+ }
691
+ }
692
+
693
+ @-moz-keyframes vex-pulse {
694
+ /* line 136, ../sass/_keyframes.sass */
695
+ 0% {
696
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
697
+ -moz-box-shadow: inset 0 0 0 300px transparent;
698
+ box-shadow: inset 0 0 0 300px transparent;
699
+ }
700
+
701
+ /* line 138, ../sass/_keyframes.sass */
702
+ 70% {
703
+ -webkit-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
704
+ -moz-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
705
+ box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
706
+ }
707
+
708
+ /* line 140, ../sass/_keyframes.sass */
709
+ 100% {
710
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
711
+ -moz-box-shadow: inset 0 0 0 300px transparent;
712
+ box-shadow: inset 0 0 0 300px transparent;
713
+ }
714
+ }
715
+
716
+ @-ms-keyframes vex-pulse {
717
+ /* line 136, ../sass/_keyframes.sass */
718
+ 0% {
719
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
720
+ -moz-box-shadow: inset 0 0 0 300px transparent;
721
+ box-shadow: inset 0 0 0 300px transparent;
722
+ }
723
+
724
+ /* line 138, ../sass/_keyframes.sass */
725
+ 70% {
726
+ -webkit-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
727
+ -moz-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
728
+ box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
729
+ }
730
+
731
+ /* line 140, ../sass/_keyframes.sass */
732
+ 100% {
733
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
734
+ -moz-box-shadow: inset 0 0 0 300px transparent;
735
+ box-shadow: inset 0 0 0 300px transparent;
736
+ }
737
+ }
738
+
739
+ @-o-keyframes vex-pulse {
740
+ /* line 136, ../sass/_keyframes.sass */
741
+ 0% {
742
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
743
+ -moz-box-shadow: inset 0 0 0 300px transparent;
744
+ box-shadow: inset 0 0 0 300px transparent;
745
+ }
746
+
747
+ /* line 138, ../sass/_keyframes.sass */
748
+ 70% {
749
+ -webkit-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
750
+ -moz-box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
751
+ box-shadow: inset 0 0 0 300px rgba(255, 255, 255, 0.25);
752
+ }
753
+
754
+ /* line 140, ../sass/_keyframes.sass */
755
+ 100% {
756
+ -webkit-box-shadow: inset 0 0 0 300px transparent;
757
+ -moz-box-shadow: inset 0 0 0 300px transparent;
758
+ box-shadow: inset 0 0 0 300px transparent;
759
+ }
760
+ }
761
+
762
+ /* line 13, ../sass/vex-theme-bottom-right-corner.sass */
763
+ .vex.vex-theme-bottom-right-corner {
764
+ top: auto;
765
+ bottom: 0;
766
+ right: 0;
767
+ overflow: visible;
768
+ }
769
+ /* line 19, ../sass/vex-theme-bottom-right-corner.sass */
770
+ .vex.vex-theme-bottom-right-corner .vex-overlay {
771
+ display: none;
772
+ }
773
+ /* line 22, ../sass/vex-theme-bottom-right-corner.sass */
774
+ .vex.vex-theme-bottom-right-corner.vex-closing .vex-content {
775
+ animation: vex-slidedown 0.5s;
776
+ -webkit-animation: vex-slidedown 0.5s;
777
+ -moz-animation: vex-slidedown 0.5s;
778
+ -ms-animation: vex-slidedown 0.5s;
779
+ -o-animation: vex-slidedown 0.5s;
780
+ -webkit-backface-visibility: hidden;
781
+ }
782
+ /* line 25, ../sass/vex-theme-bottom-right-corner.sass */
783
+ .vex.vex-theme-bottom-right-corner .vex-content {
784
+ animation: vex-slideup 0.5s;
785
+ -webkit-animation: vex-slideup 0.5s;
786
+ -moz-animation: vex-slideup 0.5s;
787
+ -ms-animation: vex-slideup 0.5s;
788
+ -o-animation: vex-slideup 0.5s;
789
+ -webkit-backface-visibility: hidden;
790
+ }
791
+ /* line 28, ../sass/vex-theme-bottom-right-corner.sass */
792
+ .vex.vex-theme-bottom-right-corner .vex-content {
793
+ -webkit-border-radius: 5px 0 0 0;
794
+ -moz-border-radius: 5px 0 0 0;
795
+ -ms-border-radius: 5px 0 0 0;
796
+ -o-border-radius: 5px 0 0 0;
797
+ border-radius: 5px 0 0 0;
798
+ font-family: "Helvetica Neue", sans-serif;
799
+ background: #f0f0f0;
800
+ color: #444444;
801
+ padding: 1em;
802
+ max-width: 100%;
803
+ width: 450px;
804
+ font-size: 1.1em;
805
+ line-height: 1.5em;
806
+ position: fixed;
807
+ bottom: 0;
808
+ right: 0;
809
+ left: auto;
810
+ }
811
+ /* line 43, ../sass/vex-theme-bottom-right-corner.sass */
812
+ .vex.vex-theme-bottom-right-corner .vex-content h1, .vex.vex-theme-bottom-right-corner .vex-content h2, .vex.vex-theme-bottom-right-corner .vex-content h3, .vex.vex-theme-bottom-right-corner .vex-content h4, .vex.vex-theme-bottom-right-corner .vex-content h5, .vex.vex-theme-bottom-right-corner .vex-content h6, .vex.vex-theme-bottom-right-corner .vex-content p, .vex.vex-theme-bottom-right-corner .vex-content ul, .vex.vex-theme-bottom-right-corner .vex-content li {
813
+ color: inherit;
814
+ }
815
+ /* line 46, ../sass/vex-theme-bottom-right-corner.sass */
816
+ .vex.vex-theme-bottom-right-corner .vex-close {
817
+ -webkit-border-radius: 5px;
818
+ -moz-border-radius: 5px;
819
+ -ms-border-radius: 5px;
820
+ -o-border-radius: 5px;
821
+ border-radius: 5px;
822
+ position: absolute;
823
+ top: 0;
824
+ right: 0;
825
+ cursor: pointer;
826
+ }
827
+ /* line 53, ../sass/vex-theme-bottom-right-corner.sass */
828
+ .vex.vex-theme-bottom-right-corner .vex-close:before {
829
+ -webkit-border-radius: 3px;
830
+ -moz-border-radius: 3px;
831
+ -ms-border-radius: 3px;
832
+ -o-border-radius: 3px;
833
+ border-radius: 3px;
834
+ position: absolute;
835
+ content: "\00D7";
836
+ font-size: 26px;
837
+ font-weight: normal;
838
+ line-height: 31px;
839
+ height: 30px;
840
+ width: 30px;
841
+ text-align: center;
842
+ top: 3px;
843
+ right: 3px;
844
+ color: #bbbbbb;
845
+ background: transparent;
846
+ }
847
+ /* line 68, ../sass/vex-theme-bottom-right-corner.sass */
848
+ .vex.vex-theme-bottom-right-corner .vex-close:hover:before, .vex.vex-theme-bottom-right-corner .vex-close:active:before {
849
+ color: #777777;
850
+ background: #e0e0e0;
851
+ }
852
+ /* line 74, ../sass/vex-theme-bottom-right-corner.sass */
853
+ .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-message {
854
+ margin-bottom: 0.5em;
855
+ }
856
+ /* line 77, ../sass/vex-theme-bottom-right-corner.sass */
857
+ .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input {
858
+ margin-bottom: 1em;
859
+ }
860
+ /* line 80, ../sass/vex-theme-bottom-right-corner.sass */
861
+ .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input textarea, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="date"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="datetime"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="datetime-local"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="email"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="month"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="number"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="password"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="search"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="tel"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="text"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="time"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="url"], .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="week"] {
862
+ -webkit-border-radius: 3px;
863
+ -moz-border-radius: 3px;
864
+ -ms-border-radius: 3px;
865
+ -o-border-radius: 3px;
866
+ border-radius: 3px;
867
+ background: white;
868
+ width: 100%;
869
+ padding: 0.25em 0.67em;
870
+ border: 0;
871
+ font-family: inherit;
872
+ font-weight: inherit;
873
+ font-size: inherit;
874
+ min-height: 2.5em;
875
+ margin: 0 0 0.25em;
876
+ }
877
+ /* line 92, ../sass/vex-theme-bottom-right-corner.sass */
878
+ .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input textarea:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="date"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="datetime"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="datetime-local"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="email"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="month"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="number"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="password"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="search"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="tel"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="text"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="time"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="url"]:focus, .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-input input[type="week"]:focus {
879
+ -webkit-box-shadow: inset 0 0 0 2px #8dbdf1;
880
+ -moz-box-shadow: inset 0 0 0 2px #8dbdf1;
881
+ box-shadow: inset 0 0 0 2px #8dbdf1;
882
+ outline: none;
883
+ }
884
+ /* line 96, ../sass/vex-theme-bottom-right-corner.sass */
885
+ .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-buttons {
886
+ *zoom: 1;
887
+ }
888
+ /* line 38, ../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
889
+ .vex.vex-theme-bottom-right-corner .vex-dialog-form .vex-dialog-buttons:after {
890
+ content: "";
891
+ display: table;
892
+ clear: both;
893
+ }
894
+ /* line 99, ../sass/vex-theme-bottom-right-corner.sass */
895
+ .vex.vex-theme-bottom-right-corner .vex-dialog-button {
896
+ -webkit-border-radius: 3px;
897
+ -moz-border-radius: 3px;
898
+ -ms-border-radius: 3px;
899
+ -o-border-radius: 3px;
900
+ border-radius: 3px;
901
+ border: 0;
902
+ float: right;
903
+ margin: 0 0 0 0.5em;
904
+ font-family: inherit;
905
+ text-transform: uppercase;
906
+ letter-spacing: 0.1em;
907
+ font-size: 0.8em;
908
+ line-height: 1em;
909
+ padding: 0.75em 2em;
910
+ }
911
+ /* line 111, ../sass/vex-theme-bottom-right-corner.sass */
912
+ .vex.vex-theme-bottom-right-corner .vex-dialog-button.vex-last {
913
+ margin-left: 0;
914
+ }
915
+ /* line 114, ../sass/vex-theme-bottom-right-corner.sass */
916
+ .vex.vex-theme-bottom-right-corner .vex-dialog-button:focus {
917
+ animation: vex-pulse 1.1s infinite;
918
+ -webkit-animation: vex-pulse 1.1s infinite;
919
+ -moz-animation: vex-pulse 1.1s infinite;
920
+ -ms-animation: vex-pulse 1.1s infinite;
921
+ -o-animation: vex-pulse 1.1s infinite;
922
+ -webkit-backface-visibility: hidden;
923
+ outline: none;
924
+ }
925
+ @media (max-width: 568px) {
926
+ /* line 114, ../sass/vex-theme-bottom-right-corner.sass */
927
+ .vex.vex-theme-bottom-right-corner .vex-dialog-button:focus {
928
+ animation: none;
929
+ -webkit-animation: none;
930
+ -moz-animation: none;
931
+ -ms-animation: none;
932
+ -o-animation: none;
933
+ -webkit-backface-visibility: hidden;
934
+ }
935
+ }
936
+ /* line 123, ../sass/vex-theme-bottom-right-corner.sass */
937
+ .vex.vex-theme-bottom-right-corner .vex-dialog-button.vex-dialog-button-primary {
938
+ background: #3288e6;
939
+ color: white;
940
+ }
941
+ /* line 127, ../sass/vex-theme-bottom-right-corner.sass */
942
+ .vex.vex-theme-bottom-right-corner .vex-dialog-button.vex-dialog-button-secondary {
943
+ background: #e0e0e0;
944
+ color: #777777;
945
+ }
946
+
947
+ /* line 131, ../sass/vex-theme-bottom-right-corner.sass */
948
+ .vex-loading-spinner.vex-theme-bottom-right-corner {
949
+ -webkit-box-shadow: 0 0 0 0.5em #f0f0f0, 0 0 1px 0.5em rgba(0, 0, 0, 0.3);
950
+ -moz-box-shadow: 0 0 0 0.5em #f0f0f0, 0 0 1px 0.5em rgba(0, 0, 0, 0.3);
951
+ box-shadow: 0 0 0 0.5em #f0f0f0, 0 0 1px 0.5em rgba(0, 0, 0, 0.3);
952
+ -webkit-border-radius: 100%;
953
+ -moz-border-radius: 100%;
954
+ -ms-border-radius: 100%;
955
+ -o-border-radius: 100%;
956
+ border-radius: 100%;
957
+ background: #f0f0f0;
958
+ border: 0.2em solid transparent;
959
+ border-top-color: #bbbbbb;
960
+ top: -1.1em;
961
+ bottom: auto;
962
+ }
963
+
964
+ /* line 140, ../sass/vex-theme-bottom-right-corner.sass */
965
+ body.vex-open {
966
+ overflow: initial;
967
+ }
968
+
969
+ .vex.vex-theme-bottom-right-corner .vex-content,
970
+ .vex.vex-theme-bottom-right-corner.vex-closing .vex-content {
971
+ -webkit-animation-fill-mode: forwards;
972
+ -moz-animation-fill-mode: forwards;
973
+ -ms-animation-fill-mode: forwards;
974
+ -o-animation-fill-mode: forwards;
975
+ animation-fill-mode: forwards;
976
+ }
power-ups/subscribe-widget/frontend/js/leadin-subscribe.js ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(document).ready( function ( $ ) {
2
+ var li_subscribe_flag = $.cookie('li_subscribe');
3
+
4
+ if ( !leadin_subscribe_check_mobile($) )
5
+ {
6
+ if ( !li_subscribe_flag )
7
+ {
8
+ leadin_check_visitor_status($.cookie("li_hash"), function ( data ) {
9
+ if ( data != 'subscribe' )
10
+ {
11
+ $.cookie("li_subscribe", 'show', {path: "/", domain: ""});
12
+ bind_leadin_subscribe_widget();
13
+ }
14
+ else
15
+ {
16
+ $.cookie("li_subscribe", 'ignore', {path: "/", domain: ""});
17
+ }
18
+ });
19
+ }
20
+ else
21
+ {
22
+ if ( li_subscribe_flag == 'show' )
23
+ bind_leadin_subscribe_widget();
24
+ }
25
+ }
26
+ });
27
+
28
+ function bind_leadin_subscribe_widget ()
29
+ {
30
+ (function(){
31
+ var $ = jQuery;
32
+ var subscribe = {};
33
+
34
+ subscribe.vex = undefined;
35
+
36
+ subscribe.init = function() {
37
+ $(window).scroll(function() {
38
+ if ($(window).scrollTop() + $(window).height() > $(document).height() / 2) {
39
+ subscribe.open();
40
+ } else {
41
+ subscribe.close();
42
+ }
43
+ });
44
+ };
45
+
46
+ subscribe.open = function() {
47
+ if (subscribe.vex) {
48
+ return subscribe._open();
49
+ }
50
+
51
+ subscribe.vex = vex.dialog.open({
52
+ showCloseButton: true,
53
+ className: 'leadin-subscribe vex-theme-bottom-right-corner',
54
+ message: $('#leadin-subscribe-heading').val(),
55
+ input: '' +
56
+ '<input id="leadin-subscribe-email" name="email" type="email" placeholder="Email address" required />' +
57
+ '<input id="leadin-subscribe-first-name" name="firstName" type="text" placeholder="First name" required/>' +
58
+ '<input id="leadin-subscribe-last-name" name="lastName" type="text" placeholder="Last name" required/>',
59
+ buttons: [$.extend({}, vex.dialog.buttons.YES, { text: 'Subscribe' })],
60
+ callback: function(data) {
61
+ if (data === false) {
62
+ $.cookie("li_subscribe", 'ignore', {path: "/", domain: ""});
63
+ return;
64
+ }
65
+
66
+ leadin_submit_form($('.leadin-subscribe form'), $, 'subscribe');
67
+ $.cookie("li_subscribe", 'ignore', {path: "/", domain: ""});
68
+ }
69
+ });
70
+
71
+ $('.leadin-subscribe form.vex-dialog-form').append('<a href="http://leadin.com/pop-subscribe-form-plugin-wordpress/?source=widget&referrer=' + document.URL + '" id="leadin-subscribe-powered-by" class="leadin-subscribe-powered-by">Powered by LeadIn</a>');
72
+ };
73
+
74
+ subscribe._open = function() {
75
+ subscribe.vex.parent().removeClass('vex-closing');
76
+ }
77
+
78
+ subscribe.close = function() {
79
+
80
+ if (!subscribe.vex) {
81
+ return;
82
+ }
83
+ subscribe.vex.parent().addClass('vex-closing');
84
+ }
85
+
86
+ subscribe.init();
87
+ window.subscribe = subscribe;
88
+ })();
89
+ }
90
+
91
+ function leadin_subscribe_check_mobile( $ )
92
+ {
93
+ var is_mobile = false;
94
+
95
+ if ( $('#leadin-subscribe-mobile-check').css('display')=='none' )
96
+ is_mobile = true;
97
+
98
+ return is_mobile;
99
+ }
power-ups/subscribe-widget/frontend/js/vex.dialog.js ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function() {
2
+ var vexDialogFactory;
3
+
4
+ vexDialogFactory = function($, vex) {
5
+ var $formToObject, dialog;
6
+ if (vex == null) {
7
+ return $.error('Vex is required to use vex.dialog');
8
+ }
9
+ $formToObject = function($form) {
10
+ var object;
11
+ object = {};
12
+ $.each($form.serializeArray(), function() {
13
+ if (object[this.name]) {
14
+ if (!object[this.name].push) {
15
+ object[this.name] = [object[this.name]];
16
+ }
17
+ return object[this.name].push(this.value || '');
18
+ } else {
19
+ return object[this.name] = this.value || '';
20
+ }
21
+ });
22
+ return object;
23
+ };
24
+ dialog = {};
25
+ dialog.buttons = {
26
+ YES: {
27
+ text: 'OK',
28
+ type: 'submit',
29
+ className: 'vex-dialog-button-primary'
30
+ },
31
+ NO: {
32
+ text: 'Cancel',
33
+ type: 'button',
34
+ className: 'vex-dialog-button-secondary',
35
+ click: function($vexContent, event) {
36
+ $vexContent.data().vex.value = false;
37
+ return vex.close($vexContent.data().vex.id);
38
+ }
39
+ }
40
+ };
41
+ dialog.defaultOptions = {
42
+ callback: function(value) {},
43
+ afterOpen: function() {},
44
+ message: 'Message',
45
+ input: "<input name=\"vex\" type=\"hidden\" value=\"_vex-empty-value\" />",
46
+ value: false,
47
+ buttons: [dialog.buttons.YES, dialog.buttons.NO],
48
+ showCloseButton: false,
49
+ onSubmit: function(event) {
50
+ var $form, $vexContent;
51
+ $form = $(this);
52
+ $vexContent = $form.parent();
53
+ event.preventDefault();
54
+ event.stopPropagation();
55
+ $vexContent.data().vex.value = dialog.getFormValueOnSubmit($formToObject($form));
56
+ return vex.close($vexContent.data().vex.id);
57
+ },
58
+ focusFirstInput: true
59
+ };
60
+ dialog.defaultAlertOptions = {
61
+ message: 'Alert',
62
+ buttons: [dialog.buttons.YES]
63
+ };
64
+ dialog.defaultConfirmOptions = {
65
+ message: 'Confirm'
66
+ };
67
+ dialog.open = function(options) {
68
+ var $vexContent;
69
+ options = $.extend({}, vex.defaultOptions, dialog.defaultOptions, options);
70
+ options.content = dialog.buildDialogForm(options);
71
+ options.beforeClose = function($vexContent) {
72
+ return options.callback($vexContent.data().vex.value);
73
+ };
74
+ $vexContent = vex.open(options);
75
+ if (options.focusFirstInput) {
76
+ $vexContent.find('input[type="submit"], textarea, input[type="date"], input[type="datetime"], input[type="datetime-local"], input[type="email"], input[type="month"], input[type="number"], input[type="password"], input[type="search"], input[type="tel"], input[type="text"], input[type="time"], input[type="url"], input[type="week"]').first().focus();
77
+ }
78
+ return $vexContent;
79
+ };
80
+ dialog.alert = function(options) {
81
+ if (typeof options === 'string') {
82
+ options = {
83
+ message: options
84
+ };
85
+ }
86
+ options = $.extend({}, dialog.defaultAlertOptions, options);
87
+ return dialog.open(options);
88
+ };
89
+ dialog.confirm = function(options) {
90
+ if (typeof options === 'string') {
91
+ return $.error('dialog.confirm(options) requires options.callback.');
92
+ }
93
+ options = $.extend({}, dialog.defaultConfirmOptions, options);
94
+ return dialog.open(options);
95
+ };
96
+ dialog.prompt = function(options) {
97
+ var defaultPromptOptions;
98
+ if (typeof options === 'string') {
99
+ return $.error('dialog.prompt(options) requires options.callback.');
100
+ }
101
+ defaultPromptOptions = {
102
+ message: "<label for=\"vex\">" + (options.label || 'Prompt:') + "</label>",
103
+ input: "<input name=\"vex\" type=\"text\" class=\"vex-dialog-prompt-input\" placeholder=\"" + (options.placeholder || '') + "\" value=\"" + (options.value || '') + "\" />"
104
+ };
105
+ options = $.extend({}, defaultPromptOptions, options);
106
+ return dialog.open(options);
107
+ };
108
+ dialog.buildDialogForm = function(options) {
109
+ var $form, $input, $message;
110
+ $form = $('<form class="vex-dialog-form" />');
111
+ $message = $('<div class="vex-dialog-message" />');
112
+ $input = $('<div class="vex-dialog-input" />');
113
+ $form.append($message.append(options.message)).append($input.append(options.input)).append(dialog.buttonsToDOM(options.buttons)).bind('submit.vex', options.onSubmit);
114
+ return $form;
115
+ };
116
+ dialog.getFormValueOnSubmit = function(formData) {
117
+ if (formData.vex || formData.vex === '') {
118
+ if (formData.vex === '_vex-empty-value') {
119
+ return true;
120
+ }
121
+ return formData.vex;
122
+ } else {
123
+ return formData;
124
+ }
125
+ };
126
+ dialog.buttonsToDOM = function(buttons) {
127
+ var $buttons;
128
+ $buttons = $('<div class="vex-dialog-buttons" />');
129
+ $.each(buttons, function(index, button) {
130
+ return $buttons.append($("<input type=\"" + button.type + "\" />").val(button.text).addClass(button.className + ' vex-dialog-button ' + (index === 0 ? 'vex-first ' : '') + (index === buttons.length - 1 ? 'vex-last ' : '')).bind('click.vex', function(e) {
131
+ if (button.click) {
132
+ return button.click($(this).parents("." + vex.baseClassNames.content), e);
133
+ }
134
+ }));
135
+ });
136
+ return $buttons;
137
+ };
138
+ return dialog;
139
+ };
140
+
141
+ if (typeof define === 'function' && define.amd) {
142
+ define(['jquery', 'vex'], vexDialogFactory);
143
+ } else if (typeof exports === 'object') {
144
+ module.exports = vexDialogFactory(require('jquery'), require('vex'));
145
+ } else {
146
+ window.vex.dialog = vexDialogFactory(window.jQuery, window.vex);
147
+ }
148
+
149
+ }).call(this);
power-ups/subscribe-widget/frontend/js/vex.js ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function() {
2
+ var vexFactory;
3
+
4
+ vexFactory = function($) {
5
+ var animationEndSupport, vex;
6
+ animationEndSupport = false;
7
+ $(function() {
8
+ var s;
9
+ s = (document.body || document.documentElement).style;
10
+ animationEndSupport = s.animation !== void 0 || s.WebkitAnimation !== void 0 || s.MozAnimation !== void 0 || s.MsAnimation !== void 0 || s.OAnimation !== void 0;
11
+ return $(window).bind('keyup.vex', function(event) {
12
+ if (event.keyCode === 27) {
13
+ return vex.closeByEscape();
14
+ }
15
+ });
16
+ });
17
+ return vex = {
18
+ globalID: 1,
19
+ animationEndEvent: 'animationend webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend',
20
+ baseClassNames: {
21
+ vex: 'vex',
22
+ content: 'vex-content',
23
+ overlay: 'vex-overlay',
24
+ close: 'vex-close',
25
+ closing: 'vex-closing',
26
+ open: 'vex-open'
27
+ },
28
+ defaultOptions: {
29
+ content: '',
30
+ showCloseButton: true,
31
+ escapeButtonCloses: true,
32
+ overlayClosesOnClick: true,
33
+ appendLocation: 'body',
34
+ className: '',
35
+ css: {},
36
+ overlayClassName: '',
37
+ overlayCSS: {},
38
+ contentClassName: '',
39
+ contentCSS: {},
40
+ closeClassName: '',
41
+ closeCSS: {}
42
+ },
43
+ open: function(options) {
44
+ options = $.extend({}, vex.defaultOptions, options);
45
+ options.id = vex.globalID;
46
+ vex.globalID += 1;
47
+ options.$vex = $('<div>').addClass(vex.baseClassNames.vex).addClass(options.className).css(options.css).data({
48
+ vex: options
49
+ });
50
+ options.$vexOverlay = $('<div>').addClass(vex.baseClassNames.overlay).addClass(options.overlayClassName).css(options.overlayCSS).data({
51
+ vex: options
52
+ });
53
+ if (options.overlayClosesOnClick) {
54
+ options.$vexOverlay.bind('click.vex', function(e) {
55
+ if (e.target !== this) {
56
+ return;
57
+ }
58
+ return vex.close($(this).data().vex.id);
59
+ });
60
+ }
61
+ options.$vex.append(options.$vexOverlay);
62
+ options.$vexContent = $('<div>').addClass(vex.baseClassNames.content).addClass(options.contentClassName).css(options.contentCSS).append(options.content).data({
63
+ vex: options
64
+ });
65
+ options.$vex.append(options.$vexContent);
66
+ if (options.showCloseButton) {
67
+ options.$closeButton = $('<div>').addClass(vex.baseClassNames.close).addClass(options.closeClassName).css(options.closeCSS).data({
68
+ vex: options
69
+ }).bind('click.vex', function() {
70
+ return vex.close($(this).data().vex.id);
71
+ });
72
+ options.$vexContent.append(options.$closeButton);
73
+ }
74
+ $(options.appendLocation).append(options.$vex);
75
+ vex.setupBodyClassName(options.$vex);
76
+ if (options.afterOpen) {
77
+ options.afterOpen(options.$vexContent, options);
78
+ }
79
+ setTimeout((function() {
80
+ return options.$vexContent.trigger('vexOpen', options);
81
+ }), 0);
82
+ return options.$vexContent;
83
+ },
84
+ getAllVexes: function() {
85
+ return $("." + vex.baseClassNames.vex + ":not(\"." + vex.baseClassNames.closing + "\") ." + vex.baseClassNames.content);
86
+ },
87
+ getVexByID: function(id) {
88
+ return vex.getAllVexes().filter(function() {
89
+ return $(this).data().vex.id === id;
90
+ });
91
+ },
92
+ close: function(id) {
93
+ var $lastVex;
94
+ if (!id) {
95
+ $lastVex = vex.getAllVexes().last();
96
+ if (!$lastVex.length) {
97
+ return false;
98
+ }
99
+ id = $lastVex.data().vex.id;
100
+ }
101
+ return vex.closeByID(id);
102
+ },
103
+ closeAll: function() {
104
+ var ids;
105
+ ids = vex.getAllVexes().map(function() {
106
+ return $(this).data().vex.id;
107
+ }).toArray();
108
+ if (!(ids != null ? ids.length : void 0)) {
109
+ return false;
110
+ }
111
+ $.each(ids.reverse(), function(index, id) {
112
+ return vex.closeByID(id);
113
+ });
114
+ return true;
115
+ },
116
+ closeByID: function(id) {
117
+ var $vex, $vexContent, beforeClose, close, options;
118
+ $vexContent = vex.getVexByID(id);
119
+ if (!$vexContent.length) {
120
+ return;
121
+ }
122
+ $vex = $vexContent.data().vex.$vex;
123
+ options = $.extend({}, $vexContent.data().vex);
124
+ beforeClose = function() {
125
+ if (options.beforeClose) {
126
+ return options.beforeClose($vexContent, options);
127
+ }
128
+ };
129
+ close = function() {
130
+ $vexContent.trigger('vexClose', options);
131
+ $vex.remove();
132
+ if (options.afterClose) {
133
+ return options.afterClose($vexContent, options);
134
+ }
135
+ };
136
+ if (animationEndSupport) {
137
+ beforeClose();
138
+ $vex.unbind(vex.animationEndEvent).bind(vex.animationEndEvent, function() {
139
+ return close();
140
+ }).addClass(vex.baseClassNames.closing);
141
+ } else {
142
+ beforeClose();
143
+ close();
144
+ }
145
+ return true;
146
+ },
147
+ closeByEscape: function() {
148
+ var $lastVex, id, ids;
149
+ ids = vex.getAllVexes().map(function() {
150
+ return $(this).data().vex.id;
151
+ }).toArray();
152
+ if (!(ids != null ? ids.length : void 0)) {
153
+ return false;
154
+ }
155
+ id = Math.max.apply(Math, ids);
156
+ $lastVex = vex.getVexByID(id);
157
+ if ($lastVex.data().vex.escapeButtonCloses !== true) {
158
+ return false;
159
+ }
160
+ return vex.closeByID(id);
161
+ },
162
+ setupBodyClassName: function($vex) {
163
+ return $vex.bind('vexOpen.vex', function() {
164
+ return $('body').addClass(vex.baseClassNames.open);
165
+ }).bind('vexClose.vex', function() {
166
+ if (!vex.getAllVexes().length) {
167
+ return $('body').removeClass(vex.baseClassNames.open);
168
+ }
169
+ });
170
+ },
171
+ hideLoading: function() {
172
+ return $('.vex-loading-spinner').remove();
173
+ },
174
+ showLoading: function() {
175
+ vex.hideLoading();
176
+ return $('body').append("<div class=\"vex-loading-spinner " + vex.defaultOptions.className + "\"></div>");
177
+ }
178
+ };
179
+ };
180
+
181
+ if (typeof define === 'function' && define.amd) {
182
+ define(['jquery'], vexFactory);
183
+ } else if (typeof exports === 'object') {
184
+ module.exports = vexFactory(require('jquery'));
185
+ } else {
186
+ window.vex = vexFactory(jQuery);
187
+ }
188
+
189
+ }).call(this);
readme.txt ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === LeadIn ===
2
+ Contributors: andygcook, nelsonjoyce
3
+ Tags: lead tracking, visitor tracking, analytics, crm, marketing automation, inbound marketing, subscription, marketing, lead generation
4
+ Requires at least: 3.3
5
+ Tested up to: 3.8
6
+ Stable tag: 0.6.0
7
+
8
+ LeadIn - WordPress CRM
9
+
10
+ == Description ==
11
+
12
+ = Get personal with your leads =
13
+
14
+ <a href="http://leadin.com" alt="WordPress marketing automation and lead tracking plugin">LeadIn</a> is an easy-to-use marketing automation and lead tracking plugin for WordPress that helps you better understand your web site visitors.
15
+
16
+ When a person submits a form on your WordPress site, you want to know more about them. What pages they've visited, when they return, and what social networks they’re on. Our WordPress marketing automation and lead tracking plugin gives you the details you need to make your next move. Because business isn’t business unless it’s personal.
17
+
18
+ = How does it work? =
19
+
20
+ 1. When you activate the WordPress plugin, LeadIn will track each anonymous visitor to your site with a cookie. If someone closes the pop-up or subscribes, we won't show them the pop-up again.
21
+ 2. Once someone fills out the subscribe form or any other other form on your site, LeadIn will identify that person with their email address.
22
+ 3. You'll receive an email with a link to the new contact record with all of their visit history.
23
+
24
+ = Who's using LeadIn? =
25
+
26
+
27
+ **Alan Perlman**: *“I can use LeadIn to get a sense of how engaged certain contacts are, and I can learn more about their behavior on my website to better drive the conversation and understand what they’re interested in or looking for.”*
28
+
29
+ <a href="http://www.extremeinbound.com/leadin-wordpress-crm-inbound-plugin/">Read more from Alan</a>
30
+
31
+
32
+ **Adam W. Warner**: *“…the LeadIn plugin has been very useful so far in giving us an idea of the actual visitor paths to our contact forms vs. the paths we’ve intended.”*
33
+
34
+ <a href="http://thewpvalet.com/wordpress-lead-tracking/">Read more from Adam</a>
35
+
36
+
37
+ = Note: =
38
+
39
+ LeadIn collects usage information about this plugin so that we can better serve our customers and know what features to add. By installing and activating the LeadIn for WordPress plugin you agree to these terms.
40
+
41
+ == Installation ==
42
+
43
+ 1. Upload the 'leadin' folder to the '/wp-content/plugins/' directory
44
+ 2. Activate the plugin through the 'Plugins' menu in WordPress
45
+ 3. Add an email address under 'LeadIn' in your settings panel
46
+
47
+ == Frequently Asked Questions ==
48
+
49
+ = How does LeadIn integrate with my forms? =
50
+
51
+ LeadIn automatically integrates with your contact and comment forms that contain an email address field on your web site. There's no setup required.
52
+
53
+ = When does LeadIn create a contact in my CRM? =
54
+
55
+ LeadIn creates a new contact in your CRM whenever an email address is detected in your visitor's form submission.
56
+
57
+ = Which contact form building plugins are supported? =
58
+
59
+ LeadIn is intended to work with any HTML form out of the box, but does not support forms created by Javascript or loaded through an iFrame.
60
+
61
+ To ensure quality we've tested the most popular WordPress form builder plugins.
62
+
63
+ = Tested + supported: =
64
+
65
+ - Contact Form 7
66
+ - JetPack
67
+ - Fast Secure Contact Form
68
+ - Contact Form
69
+ - Gravity Forms
70
+ - Formidable
71
+ - Ninja Forms
72
+ - Contact Form Clean and Simple
73
+
74
+ = Tested + unsupported: =
75
+
76
+ - Wufoo
77
+ - HubSpot
78
+ - Easy Contact Forms
79
+ - Disqus comments
80
+
81
+ == Screenshots ==
82
+
83
+ 1. Individual contact history
84
+ 2. Contacts list
85
+ 3. Sample email report
86
+
87
+ == Changelog ==
88
+
89
+ Current version: 0.6.0
90
+ Current version release: 2014-03-07
91
+
92
+ = 0.6.0 (2014.03.07) =
93
+ - Bug fixes
94
+ - Remove in-house plugin updating functionality
95
+ - Original referrer is always the server url, not the HTTP referrer
96
+ - Strip slashes from title tags
97
+ - Number of contacts does not equal leads + commenters + subscribers
98
+ - Modals aren't bound to forms after page load
99
+ - Fix bug with activating + reactivating the plugin overwriting the saved settings
100
+ - Override button styles for Subscription Pop-in widget
101
+
102
+ = Enhancements =
103
+ - Improved readability on new lead notification emails
104
+ - Confirmation email added for new subscribers to the LeadIn Subscribe Pop-in
105
+ - Updated screenshots
106
+ - Improved onboarding flow
107
+ - Deleted unused and deprecated files
108
+
109
+ = 0.5.1 (2014.03.03) =
110
+ - Bug fixes
111
+ - Fixed Subscribe Pop-in automatically enabling itself
112
+
113
+ = 0.5.0 (2014.02.25) =
114
+ - Bug fixes
115
+ - Add (blank page title tag) to emails and contact timeline for blank page titles
116
+ - Fix link on admin nav menu bar to link to contact list
117
+ - Ignore lead notifications and subscribe popup on login page
118
+ - Saving an email no longer overwrites all the LeadIn options
119
+ - Added live chat support
120
+
121
+ = Enhancements =
122
+ - New power-ups page
123
+ - LeadIn Subscribe integrated into plugin as a power-up
124
+ - Improved contact history styling + interface
125
+ - Added visit, pageview and submission stats to the contact view
126
+ - Added Live Chat into the LeadIn WordPress admin screens
127
+ - New LeadIn icons for WordPres sidebar and admin nav menu
128
+
129
+ = 0.4.6 (2013.02.11) =
130
+ - Bug fixes
131
+ - Fix table sorting for integers
132
+ - Bug fixes to contact type headings
133
+ - Bug fix "Select All" export
134
+ - Bug fix for CSS "page views" hover triangle breaking to next line
135
+ - Backwards compability for < jQuery 1.7.0
136
+ - Add LeadIn link to admin bar
137
+
138
+ = Enhancements =
139
+ - New onboarding flow
140
+
141
+ = 0.4.5 (2013.01.30) =
142
+ = Enhancements =
143
+ - Integration with LeadIn Subscribe
144
+
145
+ = 0.4.4 (2013.01.24) =
146
+ - Bug fixes
147
+ - Bind submission tracking on buttons and images inside of forms instead of just submit input types
148
+
149
+ = Enhancements =
150
+ - Change out screenshots to obfiscate personal information
151
+
152
+ = 0.4.3 (2013.01.13) =
153
+ - Bug fixes
154
+ - Fixed LeadIn form submission inserts for comments
155
+ - Resolved various silent PHP warnings in administrative dashboard
156
+ - Fixed LeadIn updater class to be compatible with WP3.8
157
+ - Improved contact merging logic to be more reliable
158
+
159
+ = Enhancements =
160
+ - Improved onboarding flow
161
+ - Optimized form submission catching + improved performance
162
+
163
+ = 0.4.2 (2013.12.30) =
164
+ - Bug fixes
165
+ - Change 'contact' to 'lead' in the contacts table
166
+ - Fixed emails always sending to the admin_email
167
+ - Tie historical events to new lead when an email is submitted multiple times with different tracking codes
168
+ - Select leads, commenters and subscribers on distinct email addresses
169
+ - Fixed timeline order to show visit, then a form submission, then subsequent visits
170
+
171
+ = Enhancements =
172
+ - Added url for each page views in the contact timeline
173
+ - Added source for each visit event
174
+ - Tweak colors for contact timeline
175
+ - Default the LeadIn menu to the contacts page
176
+
177
+ = 0.4.1 (2013.12.18) =
178
+ - Bug fixes
179
+ - Removed LeadIn header from the contact timeline view
180
+ - Updated the wording on the menu view picker above contacts list
181
+ - Remove pre-mp6 styles if MP6 plugin is activated
182
+ - Default totals leads/comments = 0 when leads table is empty instead of printing blank integer
183
+ - Legacy visitors in table have 0 visits because session support did not exist. Default to 1
184
+ - Update ouput for the number of comments to be equal to total_comments, not total_leads
185
+ - Added border to pre-mp6 timeline events
186
+
187
+ = 0.4.0 (2013.12.16) =
188
+ - Bug fixes
189
+ - Block admin comment replies from creating a contact
190
+ - Fixed faulty sorting by Last visit + Created on dates in contacts list
191
+
192
+ = Enhancements =
193
+ - Timeline view of a contact history
194
+ - New CSS styles for contacts table
195
+ - Multiple email address support for new lead/comment emails
196
+ - Integration + testing for popular WordPress form builder plugins
197
+ - One click updates for manually hosted plugin
198
+
199
+ = 0.3.0 (2013.12.09) =
200
+ - Bug fixes
201
+ - HTML encoded page titles to fix broken HTML characters
202
+ - Strip slashes from page titles in emails
203
+
204
+ = Enhancements =
205
+ - Created separate LeadIn menu in WordPress admin
206
+ - CRM list of all contacts
207
+ - Added ability to export list of contacts
208
+ - LeadIn now distinguishes between a contact requests and comment submissions
209
+ - Added link to CRM list inside each contact/comment email
210
+
211
+ = 0.2.0 (2013.11.26) =
212
+ - Bug fixes
213
+ - Broke up page view history by session instead of days
214
+ - Fixed truncated form submission titles
215
+ - Updated email headers
216
+
217
+ = Enhancements =
218
+ - Plugin now updates upon activation and keeps record of version
219
+ - Added referral source to each session
220
+ - Added link to page for form submissions
221
+ - Updated email subject line
222
+ - Added social media avatars to emails
223
+
224
+ = 0.1.0 (2013.11.22) =
225
+ - Plugin released
screenshot-1.png ADDED
Binary file
screenshot-2.png ADDED
Binary file
screenshot-3.png ADDED
Binary file