<?php
/**
*
* Usage Tracking for CRM insights
* is only included if the setting
* is enabled
*
* @since 4.3.0
*
*/
class jpcrm_usage_tracking{
protected $setting_key = 'teammember_usage';
public function __construct() {
$this->init_hooks();
}
public function init_hooks(){
#} Usage tracking (pageviews) sent via AJAX
add_action('admin_footer', array($this, 'tracking_footer'));
add_action('wp_ajax_jpcrm_send_page_view', array($this, 'send_page_view'));
}
/**
* AJAX function to send the pageview
*/
public function send_page_view(){
global $zbs;
$res = array();
// check nonce
check_ajax_referer( 'jpcrm_usage_tracking_nonce', 'security' );
// retrieve page_name
$page_name = sanitize_text_field($_POST['page_name']);
// pool data
$data = array(
'action' => 'jpcrm_track_usage',
'event_name' => $page_name,
'event_type' => 'page_view',
'site_url' => home_url()
);
// call
$response = wp_remote_post( $zbs->urls['usage'], array(
'method' => 'POST',
'timeout' => 45,
'redirection' => 5,
'httpversion' => '1.0',
'blocking' => true,
'headers' => array(),
'body' => $data,
'cookies' => array()
)
);
$res['pageview'] = 'sent';
// send summary snapshot based on a transient
if(!get_transient('jpcrm_crm_snapshot')){
$this->send_snapshot();
$res['snapshot'] = 'sent';
set_transient( 'jpcrm_crm_snapshot', true, DAY_IN_SECONDS );
}
wp_send_json($res);
}
/**
* send summary snapshot
*/
public function send_snapshot(){
global $zbs, $wp_version;
// build data
$contacts_count = $zbs->DAL->contacts->getFullCount();
$contacts_woo_count = $zbs->DAL->contacts->getContacts( array(
'externalSource' => 'woo',
'count' => true
));
$contacts_with_extsource = $zbs->DAL->contacts->getTotalExtSourceCount();
$companies_count = $zbs->DAL->companies->getFullCount();
$transactions_count = $zbs->DAL->transactions->getFullCount();
$quotes_count = $zbs->DAL->quotes->getFullCount();
$invoices_count = $zbs->DAL->invoices->getFullCount();
$forms_count = $zbs->DAL->forms->getFullCount();
$tasks_count = $zbs->DAL->events->getFullCount();
$crm_users = $this->get_teammates_count( true );
$extensions_count = zeroBSCRM_extensionsInstalledCount();
$modules_count = jpcrm_core_modules_installed_count();
$wp_version = $wp_version;
$crm_version = $zbs::VERSION;
$dal_version = $zbs->dal_version;
$php_version = PHP_VERSION;
$mysql_version = zeroBSCRM_database_getVersion();
$data = array(
'action' => 'jpcrm_track_stats',
'site_url' => home_url(),
'contacts_count' => $contacts_count,
'contacts_woo_count' => $contacts_woo_count,
'contacts_with_extsource' => $contacts_with_extsource,
'companies_count' => $companies_count,
'transactions_count' => $transactions_count,
'quotes_count' => $quotes_count,
'invoices_count' => $invoices_count,
'forms_count' => $forms_count,
'events_count' => $tasks_count,
'users_count' => $crm_users,
'extensions_count' => $extensions_count,
'modules_count' => $modules_count,
'wp_version' => $wp_version,
'crm_version' => $crm_version,
'dal_version' => $dal_version,
'php_version' => $php_version,
'mysql_version' => $mysql_version,
);
// call
return wp_remote_post( $zbs->urls['usage'], array(
'method' => 'POST',
'timeout' => 45,
'redirection' => 5,
'httpversion' => '1.0',
'blocking' => true,
'headers' => array(),
'body' => $data,
'cookies' => array()
)
);
}
/**
* Package admin page as string which can be passed to usage tracking API
*/
public function get_jpcrm_admin_page(){
// retrieve uri
$uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
$uri = preg_replace( '|^.*/wp-admin/|i', '', $uri );
// if somehow failed, return
if ( ! $uri ) {
return '';
}
// hard remove any unwanted get parameters here
$uri = remove_query_arg( array( '_wpnonce' ), admin_url( $uri ) );
// get _GET Parameters
$parameters = jpcrm_url_get_params( $uri );
// cycle through some key parameters and tokenise for anonymity / cleanliness
if ( is_array( $parameters ) ) {
$return_string = '';
// required: page
if ( isset( $parameters['page'] ) && !empty ( $parameters['page'] ) ){
$return_string = $parameters['page'];
} else {
// missing $page, skip
return '';
}
// Overrides:
// action: by default it's add, unless zbsid is set, then it's edit, make so:
if ( isset( $parameters['action'] ) && !empty( $parameters['action'] ) ){
// if action is set and no zbsid is present, it's an add
if (
$parameters['action'] == 'edit' && (
!isset( $parameters['zbsid'] ) || empty( $parameters['zbsid'] )
) ){
$parameters['action'] = 'add';
}
// note if there's an action present and zbsid, we can go ahead and remove zbsid from the return :)
if ( isset( $parameters['zbsid'] ) ){
unset( $parameters['zbsid'] );
}
}
// generic out any non-helpful data
$parameters_to_blank = array( 'zbsid', 'zbsprefillcust', 'zbsprefill', 'zbs_tag', 'quickfilters', 'sort', 'zbsowner', 'zbs_template_id' );
foreach ( $parameters_to_blank as $parameter_key ){
if ( isset( $parameters[ $parameter_key ] ) ){
// set generic value
$parameters[ $parameter_key ] = 'present';
}
}
// finally rebuild into a useful string
foreach ( $parameters as $parameter_key => $parameter_value){
// skip page as is always added above
if ( $parameter_key == 'page' ){
continue;
}
if ( !empty( $return_string ) ){
$return_string .= '|';
}
// here we check if $parameter_value might be an email
// ... designed as future-proofing and to catch any potential leaks of user data to our system
if ( zeroBSCRM_validateEmail( $parameter_value ) ) $parameter_value = '{email}';
// append return string
$return_string .= $parameter_key . ':' . $parameter_value;
}
return $return_string;
}
// fallback: retrieve page via explode
$split = explode("?page=",$uri);
$page = $split[0];
if(count($split) == 2){
$page = $split[1];
}
// returns everything after the ?page= part of the URL
return $page;
}
/**
* JS to track usage
*/
public function tracking_footer(){
global $zbs;
if ( zeroBSCRM_isAdminPage() ){
// retrieve page info
$page = $this->get_jpcrm_admin_page();
if ( !empty( $page ) ){
$this->track_specific_pageview( $page );
}
}
}
/**
* JS to track usage for a specific key
* is used by `tracking_footer()` and can be used inline throughout
* core to track extra view events
*/
public function track_specific_pageview( $page_key = '' ){
global $zbs;
if ( zeroBSCRM_isAdminPage() ){
// Where usage tracking enabled, clock that this user is a CRM team member
// noting that this never sends team-member data out of the install, only the count
$this->track_crm_teammember_usage();
if ( !empty( $page_key ) ){
?>
<script>
data = {
action: 'jpcrm_send_page_view',
page_name: '<?php echo esc_html( $page_key ); ?>',
security: '<?php echo esc_html( wp_create_nonce('jpcrm_usage_tracking_nonce') ); ?>'
};
jQuery.post(ajaxurl, data, function (response) {
//nothing to see here.
});
</script>
<?php
}
}
}
/**
* Adds user id to an option of "WP users who use the CRM" which is totalled for usage statistics
* (No specific user usage data is ever sent)
*/
private function track_crm_teammember_usage(){
global $zbs;
// retrieve existing array
$existing_teammembers = $zbs->settings->get( $this->setting_key );
// catch first call
if ( !is_array( $existing_teammembers ) ){
$existing_teammembers = array();
}
$current_user_id = get_current_user_id();
// append
if ( isset( $existing_teammembers[ $current_user_id ] ) ){
// increment
$existing_teammembers[ $current_user_id ]['count']++;
// update
$existing_teammembers[ $current_user_id ]['last_seen'] = time();
} else {
// add
$existing_teammembers[ $current_user_id ] = array( 'count' => 1, 'last_seen' => time() );
}
// update setting
$zbs->settings->update( $this->setting_key, $existing_teammembers );
}
/**
* Retrieves a count of teammates who have accessed a crm page (optionally checking wp user status)
*
* @param $check_is_current_wp_user - bool; if true check that the user_id is a current wp user
*/
public function get_teammates_count( $check_is_current_wp_crm_user = false ){
global $zbs;
// retrieve teammembers
$seen_teammembers = $zbs->settings->get( $this->setting_key );
// catch first call
if ( !is_array( $seen_teammembers ) ){
return 0;
}
// if validating current status, do that, else return count
if ( $check_is_current_wp_crm_user ){
// filter user_ids who are current wp_users
$current_wp_teammembers = array_filter( array_keys( $seen_teammembers ), 'get_userdata' );
// filter user_ids who do not have a CRM backend role (and are not admins)
// e.g. CRM admins who are now Subscribers
$current_wp_teammembers = array_filter( $current_wp_teammembers, 'zeroBSCRM_permsIsZBSUserOrAdmin' );
return count( $current_wp_teammembers );
} else {
return count( $seen_teammembers );
}
}
}