Skip to content

Commit 78c43f7

Browse files
committed
Adding the power to track lead searches and comments
1 parent a2a5188 commit 78c43f7

11 files changed

+1246
-52
lines changed

assets/css/wpl.edit-lead.css

+22-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,27 @@ float: right;
104104
#lead-searches .lead-timeline {
105105
margin-left: 10px !important;
106106
}
107+
108+
#lead-searches #search-error-count{
109+
width: 92%;
110+
margin-left: 10px;
111+
}
112+
113+
#lead-searches .search-error-icon{
114+
font-size: 54px;
115+
position: absolute;
116+
left: 30px;
117+
margin-top: 10px;
118+
}
119+
120+
#lead-searches .search-error-message{
121+
padding: 26px 10px 26px 10px;
122+
border: 1px solid #e5e5e5;
123+
border-radius: 5px;
124+
background: #fff;
125+
margin-top: 3px;
126+
margin-left: 83px;
127+
}
107128
#conversions-data-display .nav-container {
108129
margin-top: 5px;
109130
}
@@ -222,4 +243,4 @@ body .metabox-holder .postbox-container .empty-container {
222243

223244
#full-contact-icon {
224245
vertical-align:middle;
225-
}
246+
}

assets/js/wpl.admin.edit.js

+12
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ jQuery(document).ready(function ($) {
259259
the_list.tsort({attr: 'data-date', order: 'desc'});
260260
jQuery('.lead-sort-active').removeClass('lead-sort-active');
261261
jQuery(this).addClass('lead-sort-active');
262+
if(which_sort == '#lead-searches'){
263+
var errorCount = jQuery('#search-error-count').detach();
264+
jQuery('#lead-searches').append(errorCount);
265+
}
262266
});
263267

264268
jQuery('#oldest-event').click(function () {
@@ -267,6 +271,10 @@ jQuery(document).ready(function ($) {
267271
the_list.tsort({attr: 'data-date', order: 'asc'});
268272
jQuery('.lead-sort-active').removeClass('lead-sort-active');
269273
jQuery(this).addClass('lead-sort-active');
274+
if(which_sort == '#lead-searches'){
275+
var errorCount = jQuery('#search-error-count').detach();
276+
jQuery('#lead-searches').append(errorCount);
277+
}
270278
});
271279

272280
jQuery('#highest').click(function () {
@@ -290,6 +298,10 @@ jQuery(document).ready(function ($) {
290298
the_list.tsort({attr: 'data-date', order: 'desc'});
291299
jQuery(toggle_this).fadeIn(700);
292300
jQuery(".lead-item-num").show();
301+
if(which_sort == '#lead-searches'){
302+
var errorCount = jQuery('#search-error-count').detach();
303+
jQuery('#lead-searches').append(errorCount);
304+
}
293305
});
294306

295307
jQuery("body").on('click', '.lead-activity-show-all', function () {

classes/class.metaboxes.wp-lead.php

+221-41
Large diffs are not rendered by default.

classes/class.tracking.php

+253-3
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ public function __construct() {
1616
public static function add_hooks() {
1717
/* listen for set cookie calls */
1818
add_action( 'wp_head', array( __CLASS__ , 'set_cookies' ) );
19-
20-
19+
20+
/* listens for comments approval status to change and either saves them to events or removes them */
21+
add_action( 'transition_comment_status', array( __CLASS__, 'track_comment_approval' ), 10, 3 );
22+
23+
/* listens for comments that are auto approved, and saves them to events */
24+
add_action( 'wp_insert_comment', array( __CLASS__, 'track_comment_inserts'), 10, 2 );
25+
26+
/* saves user searches to events */
27+
add_action( 'wp_ajax_inbound_search_store', array( __CLASS__, 'ajax_inbound_search_store' ), 10, 1 );
28+
add_action( 'wp_ajax_nopriv_inbound_search_store', array(__CLASS__, 'ajax_inbound_search_store'), 10, 1 );
2129
}
2230

2331
/**
@@ -154,6 +162,248 @@ public static function update_page_views_object( $lead_data ) {
154162
/* Run hook that tells WordPress lead data has been updated */
155163
do_action('wplead_page_view' , $lead_data );
156164
}
165+
166+
/**
167+
* Tracks the change of comment statuses. Approved or Unapproved
168+
* @param $new_status
169+
* @param $old_status
170+
* @param $comment [the comment object]
171+
*/
172+
public static function track_comment_approval( $new_status, $old_status, $comment ){
173+
174+
/* if comment tracking is turned off exit */
175+
if( !defined( 'INBOUND_PRO_CURRENT_VERSION' ) ){
176+
if( get_option( 'wpl-main-comment-tracking', '' ) == 0 ){
177+
return;
178+
}
179+
}else{
180+
$settings = Inbound_Options_API::get_option( 'inbound-pro', 'settings', array() );
181+
if( $settings['leads']['comment-tracking'] == 0 ){
182+
return;
183+
}
184+
}
185+
186+
/* if the comment has been approved, add it to the db */
187+
if( $comment->comment_approved == 1 ){
188+
189+
/* get the lead id */
190+
$lead_id = LeadStorage::lookup_lead_by_email( $comment->comment_author_email );
191+
192+
/*if the lead exists*/
193+
if( $lead_id ){
194+
195+
/* if the comment isn't already stored */
196+
if( empty( Inbound_Events::comment_exists( $comment->comment_ID ) ) ){
197+
198+
/* exit if the comment came from a source we don't want to listen to */
199+
$agents_to_ignore = array('WooCommerce');
200+
if( in_array( $comment->comment_agent, $agents_to_ignore ) ){
201+
return;
202+
}
203+
204+
$datetime = date_i18n( 'Y-m-d G:i:s T', strtotime( $comment->comment_date ) );
205+
206+
$args = array(
207+
'page_id' => $comment->comment_post_ID,
208+
'lead_id' => $lead_id,
209+
'comment_id' => $comment->comment_ID,
210+
'event_details' => json_encode( array(
211+
'comment_id' => $comment->comment_ID,
212+
'comment_content' => ($comment->comment_content) ? $comment->comment_content : '',
213+
'comment_author' => ($comment->comment_author) ? $comment->comment_author : '',
214+
'comment_author_email' => ($comment->comment_author_email) ? $comment->comment_author_email : '',
215+
'comment_author_url' => ($comment->comment_author_url) ? $comment->comment_author_url : '',
216+
'comment_agent' => ($comment->comment_agent) ? $comment->comment_agent : '',
217+
'comment_type' => ($comment->comment_type) ? $comment->comment_type : '',
218+
'comment_parent' => ($comment->comment_parent) ? $comment->comment_parent : '',
219+
'user_id' => ($comment->user_id) ? $comment->user_id : '',
220+
'page_id' => $comment->comment_post_ID,
221+
)),
222+
'datetime' => $datetime,
223+
);
224+
/* store the approved comment event */
225+
Inbound_Events::store_comment_event( $args );
226+
}
227+
}
228+
}
229+
230+
/* if the comment was unapproved later, remove it from the db */
231+
if( $comment->comment_approved != 1 ){
232+
233+
$lead_id = LeadStorage::lookup_lead_by_email( $comment->comment_author_email );
234+
235+
if( $lead_id ){ // if there isn't a lead id, then the comment isn't stored in the db
236+
237+
Inbound_Events::remove_comment_event( (int)$comment->comment_ID );
238+
}
239+
}
240+
}
241+
242+
/**
243+
* Tracks comments that are auto approved
244+
* @param $id [comment id]
245+
* @param $comment [the comment object]
246+
*/
247+
public static function track_comment_inserts( $id, $comment ){
248+
249+
/* exit if comment tracking is turned off */
250+
if( !defined( 'INBOUND_PRO_CURRENT_VERSION' ) ){
251+
if( get_option( 'wpl-main-comment-tracking', '' ) == 0 ){
252+
return;
253+
}
254+
}else{
255+
$settings = Inbound_Options_API::get_option( 'inbound-pro', 'settings', array() );
256+
if( $settings['leads']['comment-tracking'] == 0 ){
257+
return;
258+
}
259+
}
260+
261+
if( $comment->comment_approved == 1 ){
262+
263+
$lead_id = LeadStorage::lookup_lead_by_email( $comment->comment_author_email );
264+
265+
if( $lead_id ){
266+
267+
/* exit if the comment came from a source we don't want to listen to */
268+
$agents_to_ignore = array('WooCommerce');
269+
if( in_array( $comment->comment_agent, $agents_to_ignore ) ){
270+
return;
271+
}
272+
273+
/* get the current time */
274+
$datetime = date_i18n( 'Y-m-d G:i:s T' );
275+
276+
$args = array(
277+
'page_id' => $comment->comment_post_ID,
278+
'lead_id' => $lead_id,
279+
'comment_id' => $comment->comment_ID,
280+
'event_details' => json_encode( array(
281+
'comment_id' => $comment->comment_ID,
282+
'comment_content' => ($comment->comment_content) ? $comment->comment_content : '',
283+
'comment_author' => ($comment->comment_author) ? $comment->comment_author : '',
284+
'comment_author_email' => ($comment->comment_author_email) ? $comment->comment_author_email : '',
285+
'comment_author_url' => ($comment->comment_author_url) ? $comment->comment_author_url : '',
286+
'comment_agent' => ($comment->comment_agent) ? $comment->comment_agent : '',
287+
'comment_type' => ($comment->comment_type) ? $comment->comment_type : '',
288+
'comment_parent' => ($comment->comment_parent) ? $comment->comment_parent : '',
289+
'user_id' => ($comment->user_id) ? $comment->user_id : '',
290+
'page_id' => $comment->comment_post_ID,
291+
)),
292+
'datetime' => $datetime,
293+
);
294+
295+
Inbound_Events::store_comment_event( $args );
296+
}
297+
}
298+
}
299+
300+
/**
301+
* Stores the user's searches in the database
302+
* @param $_POST['data'] (array) lead data to store
303+
* @param $_POST['nonce'] (string)
304+
*/
305+
public static function ajax_inbound_search_store(){
306+
$timezone = get_option( 'gmt_offset' );
307+
$data = json_decode( stripslashes( urldecode( $_POST['data'] ) ), true );
308+
$lead_id = intval( $_POST['lead_id'] );
309+
310+
/* sanitize the input */
311+
$clean_data = array();
312+
foreach( $data as $key => $value ){
313+
$clean_data[$key] = array_map( function( $sub_value ){
314+
315+
if( !isset( $sub_value ) && empty( $sub_value ) ){
316+
return 0;
317+
}
318+
319+
/* if the value isn't url encoded, return the sanitized value */
320+
if( urldecode( $sub_value ) === $sub_value ){
321+
return sanitize_text_field( $sub_value );
322+
}
323+
324+
return sanitize_text_field( urldecode( $sub_value ) );
325+
326+
}, $value );
327+
}
328+
329+
/* if a lead id isn't supplied directly, see if one was passed in the search data */
330+
if( empty( $lead_id ) ){
331+
$lead_id = 0;
332+
$emails = array();
333+
foreach( $clean_data as $key => $value ){
334+
if( isset( $value['lead_id'] ) && !empty( $value['lead_id'] ) ){
335+
$lead_id = $value['lead_id'];
336+
break;
337+
}
338+
339+
if( is_email( $value['email'] ) && !empty($value['email'] ) ){
340+
$email[] = $value['email'];
341+
}
342+
}
343+
}
344+
345+
/* if there isn't a lead id */
346+
if( $lead_id === 0 ){
347+
// see if the email is provided, get lead_id from that if possible
348+
if( !empty( $emails ) ){
349+
$counted_emails = array_count_values( $emails );
350+
foreach( $counted_emails as $email => $count ){
351+
$lead = get_page_by_title( $email, 'OBJECT', 'wp-lead' );
352+
if( !empty( $lead ) && !null ){
353+
$lead_id = $lead->ID;
354+
break;
355+
}
356+
}
357+
}
358+
359+
// exit if we still don't have a lead id
360+
if( $lead_id == 0 ){
361+
// echo json_encode( array( 'error' => __( 'No id provided', 'inbound-pro' ) ) );
362+
die();
363+
}
364+
}
365+
366+
if( !wp_verify_nonce( $_POST['nonce'], 'inbound_lead_' . $lead_id . '_nonce' ) ){
367+
// echo json_encode( array( 'error' => __( 'Invalid nonce', 'inbound-pro' ) ) );
368+
die();
369+
}
370+
371+
// check to see if the lead exists
372+
$lead = get_post( $lead_id );
373+
374+
// if the lead doesn't exist, exit
375+
if( !$lead ){
376+
die();
377+
}
378+
379+
foreach( $clean_data as $key => $value ){
380+
/* if the gmt offset is set, account for wp time */
381+
if( !empty( $timezone ) || $timezone === 0 ){
382+
$value['timestamp'] += ( $timezone * 3600 );
383+
}
384+
385+
$args = array(
386+
'page_id' => $value['page_id'],
387+
'lead_id' => $lead_id,
388+
'lead_uid' => $value['user_UID'],
389+
'variation_id' => $value['variation'],
390+
'event_details' => json_encode( array(
391+
'search_data' => $value['search_data'],
392+
'post_type' => $value['post_type'],
393+
'ip_address' => $value['ip_address'],
394+
'page_id' => $value['page_id'],
395+
'datetime' => date_i18n( 'Y-m-d G:i:s T', $value['timestamp'] ),
396+
) ),
397+
'source' => $value['source'],
398+
'datetime' => date( 'Y-m-d G:i:s T', $value['timestamp'] ),
399+
);
400+
401+
Inbound_Events::store_search_event( $args );
402+
}
403+
404+
wp_send_json( array( 'success' => __( 'Searches successfully stored!', 'inbound-pro' ) ) );
405+
406+
}
157407
}
158408

159-
new Leads_Tracking;
409+
new Leads_Tracking;

shared/assets/js/frontend/analytics-src/analytics.events.js

+21
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,27 @@ var _inboundEvents = (function(_inbound) {
506506
fireEvent('form_after_submission', formData);
507507

508508
},
509+
/**
510+
* `search_before_caching` is triggered before the search is stored in the user's browser.
511+
* If a lead ID is set, the search data will be saved to the server when the next page loads.
512+
* You can filter the data here or send it to third party services
513+
*
514+
* ```js
515+
* // Usage:
516+
*
517+
* // Adding the callback
518+
* function search_before_caching_function( data ) {
519+
* var data = data || {};
520+
* // filter search data
521+
* };
522+
*
523+
* // Hook the function up the the `search_before_caching` event
524+
* _inbound.add_action( 'search_before_caching', search_before_caching_function, 10 );
525+
* ```
526+
*/
527+
search_before_caching: function(searchData) {
528+
fireEvent('search_before_caching', searchData);
529+
},
509530
/*! Scrol depth https://github.com/robflaherty/jquery-scrolldepth/blob/master/jquery.scrolldepth.js */
510531

511532
analyticsError: function(MLHttpRequest, textStatus, errorThrown) {

0 commit comments

Comments
 (0)