Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 160
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
jpcrm_usage_tracking
0.00% covered (danger)
0.00%
0 / 160
0.00% covered (danger)
0.00%
0 / 9
1332
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 init_hooks
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 send_page_view
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
6
 send_snapshot
0.00% covered (danger)
0.00%
0 / 57
0.00% covered (danger)
0.00%
0 / 1
2
 get_jpcrm_admin_page
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
380
 tracking_footer
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 track_specific_pageview
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 track_crm_teammember_usage
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 get_teammates_count
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 *
4 * Usage Tracking for CRM insights
5 * is only included if the setting
6 * is enabled
7 *
8 * @since 4.3.0
9 */
10
11class jpcrm_usage_tracking {
12
13    protected $setting_key = 'teammember_usage';
14
15    public function __construct() {
16
17        $this->init_hooks();
18    }
19
20    public function init_hooks() {
21
22        #} Usage tracking (pageviews) sent via AJAX
23        add_action( 'admin_footer', array( $this, 'tracking_footer' ) );
24        add_action( 'wp_ajax_jpcrm_send_page_view', array( $this, 'send_page_view' ) );
25    }
26
27    /**
28     * AJAX function to send the pageview
29     */
30    public function send_page_view() {
31
32        global $zbs;
33
34        $res = array();
35
36        // check nonce
37        check_ajax_referer( 'jpcrm_usage_tracking_nonce', 'security' );
38
39        // retrieve page_name
40        $page_name = sanitize_text_field( $_POST['page_name'] );
41
42        // pool data
43        $data = array(
44            'action'     => 'jpcrm_track_usage',
45            'event_name' => $page_name,
46            'event_type' => 'page_view',
47            'site_url'   => home_url(),
48        );
49
50        // call
51        $response = wp_remote_post(
52            $zbs->urls['usage'],
53            array(
54                'method'      => 'POST',
55                'timeout'     => 45,
56                'redirection' => 5,
57                'httpversion' => '1.0',
58                'blocking'    => true,
59                'headers'     => array(),
60                'body'        => $data,
61                'cookies'     => array(),
62            )
63        );
64
65        $res['pageview'] = 'sent';
66
67        // send summary snapshot based on a transient
68        if ( ! get_transient( 'jpcrm_crm_snapshot' ) ) {
69
70            $this->send_snapshot();
71            $res['snapshot'] = 'sent';
72            set_transient( 'jpcrm_crm_snapshot', true, DAY_IN_SECONDS );
73
74        }
75
76        wp_send_json( $res );
77    }
78
79    /**
80     * send summary snapshot
81     */
82    public function send_snapshot() {
83
84        global $zbs, $wp_version;
85
86        // build data
87        $contacts_count     = $zbs->DAL->contacts->getFullCount();
88        $contacts_woo_count = $zbs->DAL->contacts->getContacts(
89            array(
90
91                'externalSource' => 'woo',
92                'count'          => true,
93
94            )
95        );
96        $contacts_with_extsource = $zbs->DAL->contacts->getTotalExtSourceCount();
97        $companies_count         = $zbs->DAL->companies->getFullCount();
98        $transactions_count      = $zbs->DAL->transactions->getFullCount();
99        $quotes_count            = $zbs->DAL->quotes->getFullCount();
100        $invoices_count          = $zbs->DAL->invoices->getFullCount();
101        $forms_count             = $zbs->DAL->forms->getFullCount();
102        $tasks_count             = $zbs->DAL->events->getFullCount();
103        $crm_users               = $this->get_teammates_count( true );
104        $extensions_count        = zeroBSCRM_extensionsInstalledCount();
105        $modules_count           = jpcrm_core_modules_installed_count();
106        $wp_version              = $wp_version;
107            $crm_version         = $zbs::VERSION;
108        $dal_version             = $zbs->dal_version;
109        $php_version             = PHP_VERSION;
110        $mysql_version           = zeroBSCRM_database_getVersion();
111
112        $data = array(
113            'action'                  => 'jpcrm_track_stats',
114            'site_url'                => home_url(),
115            'contacts_count'          => $contacts_count,
116            'contacts_woo_count'      => $contacts_woo_count,
117            'contacts_with_extsource' => $contacts_with_extsource,
118            'companies_count'         => $companies_count,
119            'transactions_count'      => $transactions_count,
120            'quotes_count'            => $quotes_count,
121            'invoices_count'          => $invoices_count,
122            'forms_count'             => $forms_count,
123            'events_count'            => $tasks_count,
124            'users_count'             => $crm_users,
125            'extensions_count'        => $extensions_count,
126            'modules_count'           => $modules_count,
127            'wp_version'              => $wp_version,
128            'crm_version'             => $crm_version,
129            'dal_version'             => $dal_version,
130            'php_version'             => $php_version,
131            'mysql_version'           => $mysql_version,
132        );
133
134        // call
135        return wp_remote_post(
136            $zbs->urls['usage'],
137            array(
138                'method'      => 'POST',
139                'timeout'     => 45,
140                'redirection' => 5,
141                'httpversion' => '1.0',
142                'blocking'    => true,
143                'headers'     => array(),
144                'body'        => $data,
145                'cookies'     => array(),
146            )
147        );
148    }
149
150    /**
151     * Package admin page as string which can be passed to usage tracking API
152     */
153    public function get_jpcrm_admin_page() {
154
155        // retrieve uri
156        $uri = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
157        $uri = preg_replace( '|^.*/wp-admin/|i', '', $uri );
158
159        // if somehow failed, return
160        if ( ! $uri ) {
161            return '';
162        }
163
164        // hard remove any unwanted get parameters here
165        $uri = remove_query_arg( array( '_wpnonce' ), admin_url( $uri ) );
166
167        // get _GET Parameters
168        $parameters = jpcrm_url_get_params( $uri );
169
170        // cycle through some key parameters and tokenise for anonymity / cleanliness
171        if ( is_array( $parameters ) ) {
172
173            $return_string = '';
174
175            // required: page
176            if ( isset( $parameters['page'] ) && ! empty( $parameters['page'] ) ) {
177
178                $return_string = $parameters['page'];
179
180            } else {
181
182                // missing $page, skip
183                return '';
184
185            }
186
187            // Overrides:
188
189            // action: by default it's add, unless zbsid is set, then it's edit, make so:
190            if ( isset( $parameters['action'] ) && ! empty( $parameters['action'] ) ) {
191
192                // if action is set and no zbsid is present, it's an add
193                if ( $parameters['action'] == 'edit' && (
194                    ! isset( $parameters['zbsid'] ) || empty( $parameters['zbsid'] )
195                ) ) {
196
197                    $parameters['action'] = 'add';
198                }
199
200                // note if there's an action present and zbsid, we can go ahead and remove zbsid from the return :)
201                if ( isset( $parameters['zbsid'] ) ) {
202
203                    unset( $parameters['zbsid'] );
204
205                }
206            }
207
208            // generic out any non-helpful data
209            $parameters_to_blank = array( 'zbsid', 'zbsprefillcust', 'zbsprefill', 'zbs_tag', 'quickfilters', 'sort', 'zbsowner', 'zbs_template_id' );
210
211            foreach ( $parameters_to_blank as $parameter_key ) {
212
213                if ( isset( $parameters[ $parameter_key ] ) ) {
214
215                    // set generic value
216                    $parameters[ $parameter_key ] = 'present';
217
218                }
219            }
220
221            // finally rebuild into a useful string
222            foreach ( $parameters as $parameter_key => $parameter_value ) {
223
224                // skip page as is always added above
225                if ( $parameter_key == 'page' ) {
226
227                    continue;
228
229                }
230
231                if ( ! empty( $return_string ) ) {
232
233                    $return_string .= '|';
234
235                }
236
237                // here we check if $parameter_value might be an email
238                // ... designed as future-proofing and to catch any potential leaks of user data to our system
239                if ( zeroBSCRM_validateEmail( $parameter_value ) ) {
240                    $parameter_value = '{email}';
241                }
242
243                // append return string
244                $return_string .= $parameter_key . ':' . $parameter_value;
245
246            }
247
248            return $return_string;
249
250        }
251
252        // fallback: retrieve page via explode
253        $split = explode( '?page=', $uri );
254        $page  = $split[0];
255        if ( count( $split ) == 2 ) {
256            $page = $split[1];
257        }
258
259        // returns everything after the ?page= part of the URL
260        return $page;
261    }
262
263    /**
264     * JS to track usage
265     */
266    public function tracking_footer() {
267
268        global $zbs;
269
270        if ( zeroBSCRM_isAdminPage() ) {
271
272            // retrieve page info
273            $page = $this->get_jpcrm_admin_page();
274
275            if ( ! empty( $page ) ) {
276
277                $this->track_specific_pageview( $page );
278
279            }
280        }
281    }
282
283    /**
284     * JS to track usage for a specific key
285     * is used by `tracking_footer()` and can be used inline throughout
286     * core to track extra view events
287     */
288    public function track_specific_pageview( $page_key = '' ) {
289
290        global $zbs;
291
292        if ( zeroBSCRM_isAdminPage() ) {
293
294            // Where usage tracking enabled, clock that this user is a CRM team member
295            // noting that this never sends team-member data out of the install, only the count
296            $this->track_crm_teammember_usage();
297
298            if ( ! empty( $page_key ) ) {
299
300                ?>
301                <script>
302                    data = {
303                        action: 'jpcrm_send_page_view',
304                        page_name: '<?php echo esc_html( $page_key ); ?>',
305                        security: '<?php echo esc_html( wp_create_nonce( 'jpcrm_usage_tracking_nonce' ) ); ?>'
306                    };
307                    jQuery.post(ajaxurl, data, function (response) {
308                        //nothing to see here.
309                    });
310                </script>
311                <?php
312
313            }
314        }
315    }
316
317    /**
318     * Adds user id to an option of "WP users who use the CRM" which is totalled for usage statistics
319     * (No specific user usage data is ever sent)
320     */
321    private function track_crm_teammember_usage() {
322
323        global $zbs;
324
325        // retrieve existing array
326        $existing_teammembers = $zbs->settings->get( $this->setting_key );
327
328        // catch first call
329        if ( ! is_array( $existing_teammembers ) ) {
330            $existing_teammembers = array();
331        }
332
333        $current_user_id = get_current_user_id();
334
335        // append
336        if ( isset( $existing_teammembers[ $current_user_id ] ) ) {
337
338            // increment
339            ++$existing_teammembers[ $current_user_id ]['count'];
340
341            // update
342            $existing_teammembers[ $current_user_id ]['last_seen'] = time();
343
344        } else {
345
346            // add
347            $existing_teammembers[ $current_user_id ] = array(
348                'count'     => 1,
349                'last_seen' => time(),
350            );
351
352        }
353
354        // update setting
355        $zbs->settings->update( $this->setting_key, $existing_teammembers );
356    }
357
358    /**
359     * Retrieves a count of teammates who have accessed a crm page (optionally checking wp user status)
360     *
361     * @param $check_is_current_wp_user - bool; if true check that the user_id is a current wp user
362     */
363    public function get_teammates_count( $check_is_current_wp_crm_user = false ) {
364
365        global $zbs;
366
367        // retrieve teammembers
368        $seen_teammembers = $zbs->settings->get( $this->setting_key );
369
370        // catch first call
371        if ( ! is_array( $seen_teammembers ) ) {
372            return 0;
373        }
374
375        // if validating current status, do that, else return count
376        if ( $check_is_current_wp_crm_user ) {
377
378            // filter user_ids who are current wp_users
379            $current_wp_teammembers = array_filter( array_keys( $seen_teammembers ), 'get_userdata' );
380
381            // filter user_ids who do not have a CRM backend role (and are not admins)
382            // e.g. CRM admins who are now Subscribers
383            $current_wp_teammembers = array_filter( $current_wp_teammembers, 'zeroBSCRM_permsIsZBSUserOrAdmin' );
384
385            return count( $current_wp_teammembers );
386
387        } else {
388
389            return count( $seen_teammembers );
390
391        }
392    }
393}