PluginProbe
UpdraftPlus: WP Backup & Migration Plugin / 1.16.5
UpdraftPlus: WP Backup & Migration Plugin v1.16.5
1.26.7 1.26.6 1.26.5 1.26.4 1.26.3 1.9.19 1.9.25 1.9.26 1.9.30 1.9.31 1.9.32 1.9.4 1.9.40 1.9.41 1.9.42 1.9.43 1.9.44 1.9.45 1.9.46 1.9.5 1.9.50 1.9.51 1.9.60 1.9.62 1.9.63 All 371 releases
updraftplus / central / modules / analytics.php

analytics.php in UpdraftPlus: WP Backup & Migration Plugin 1.16.5, at central/modules/analytics.php

441 lines 13.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('UPDRAFTCENTRAL_CLIENT_DIR')) die('No access.');
4
5 /**
6 * Handles Analytics Commands
7 *
8 * @method array ga_checker()
9 * @method array get_access_token()
10 * @method array set_authorization_code()
11 */
12 class UpdraftCentral_Analytics_Commands extends UpdraftCentral_Commands {
13
14 private $scope = 'https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/analytics.readonly';
15
16 private $endpoint = 'https://accounts.google.com/o/oauth2/auth';
17
18 private $token_info_endpoint = 'https://www.googleapis.com/oauth2/v1/tokeninfo';
19
20 private $access_key = 'updraftcentral_auth_server_access';
21
22 private $auth_endpoint;
23
24 private $client_id;
25
26 private $view_key = 'updraftcentral_analytics_views';
27
28 private $tracking_id_key = 'updraftcentral_analytics_tracking_id';
29
30 private $expiration;
31
32 /**
33 * Constructor
34 */
35 public function __construct() {
36 $this->auth_endpoint = defined('UPDRAFTPLUS_GOOGLECLOUD_CALLBACK_URL') ? UPDRAFTPLUS_GOOGLECLOUD_CALLBACK_URL : 'https://auth.updraftplus.com/auth/googleanalytics';
37 $this->client_id = defined('UPDRAFTPLUS_GOOGLECLOUD_CLIENT_ID') ? UPDRAFTPLUS_GOOGLECLOUD_CLIENT_ID : '306245874349-6s896c3tjpra26ns3dpplhqcl6rv6qlb.apps.googleusercontent.com';
38
39 // Set transient expiration - default for 24 hours
40 $this->expiration = 86400;
41 }
42
43 /**
44 * Checks whether Google Analytics (GA) is installed or setup
45 *
46 * N.B. This check assumes GA is installed either using "wp_head" or "wp_footer" (e.g. attached
47 * to the <head/> or somewhere before </body>). It does not recursively check all the pages
48 * of the website to find if GA is installed on each or one of those pages, but only on the main/root page.
49 *
50 * @return array $result An array containing "ga_installed" property which returns "true" if GA (Google Analytics) is installed, "false" otherwise.
51 */
52 public function ga_checker() {
53
54 try {
55
56 // Retrieves the tracking code/id if available
57 $tracking_id = $this->get_tracking_id();
58 $installed = true;
59
60 // If tracking code/id is currently not available then we
61 // parse the needed information from the buffered content through
62 // the "wp_head" and "wp_footer" hooks.
63 if (false === $tracking_id) {
64 $info = $this->extract_tracking_id();
65
66 $installed = $info['installed'];
67 $tracking_id = $info['tracking_id'];
68 }
69
70 // Get access token to be use to generate the report.
71 $access_token = $this->_get_token();
72
73 if (empty($access_token)) {
74 // If we don't get a valid access token then that would mean
75 // the access has been revoked by the user or UpdraftCentral was not authorized yet
76 // to access the user's analytics data, thus, we're clearing
77 // any previously stored user access so we're doing some housekeeping here.
78 $this->clear_user_access();
79 }
80
81 // Wrap and combined information for the requesting
82 // client's consumption
83 $result = array(
84 'ga_installed' => $installed,
85 'tracking_id' => $tracking_id,
86 'client_id' => $this->client_id,
87 'redirect_uri' => $this->auth_endpoint,
88 'scope' => $this->scope,
89 'access_token' => $access_token,
90 'endpoint' => $this->endpoint
91 );
92
93 } catch (Exception $e) {
94 $result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
95 }
96
97 return $this->_response($result);
98 }
99
100 /**
101 * Extracts Google Tracking ID from contents rendered through the "wp_head" and "wp_footer" action hooks
102 *
103 * @internal
104 * @return array $result An array containing the result of the extraction.
105 */
106 private function extract_tracking_id() {
107
108 // Define result array
109 $result = array();
110
111 // Retrieve header content
112 ob_start();
113 do_action('wp_head');
114 $header_content = ob_get_clean();
115
116 // Extract analytics information if available.
117 $output = $this->parse_content($header_content);
118 $result['installed'] = $output['installed'];
119 $result['tracking_id'] = $output['tracking_id'];
120
121 // If it was not found, then now try the footer
122 if (empty($tracking_id)) {
123 // Retrieve footer content
124 ob_start();
125 do_action('wp_footer');
126 $footer_content = ob_get_clean();
127 $output = $this->parse_content($footer_content);
128 $result['installed'] = $output['installed'];
129 $result['tracking_id'] = $output['tracking_id'];
130 }
131
132 if (!empty($tracking_id)) {
133 set_transient($this->tracking_id_key, $tracking_id, $this->expiration);
134 }
135
136 return $result;
137 }
138
139 /**
140 * Gets access token
141 *
142 * Validates whether the system currently have a valid token to use when connecting to Google Analytics API.
143 * If not, then it will send a token request based on the authorization code we stored during the
144 * authorization phase. Otherwise, it will return an empty token.
145 *
146 * @return array $result An array containing the Google Analytics API access token.
147 */
148 public function get_access_token() {
149
150 try {
151
152 // Loads or request a valid token to use
153 $access_token = $this->_get_token();
154
155 if (!empty($access_token)) {
156 $result = array('access_token' => $access_token);
157 } else {
158 $result = array('error' => true, 'message' => 'ga_token_retrieval_failed', 'values' => array());
159 }
160
161 } catch (Exception $e) {
162 $result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
163 }
164
165 return $this->_response($result);
166 }
167
168 /**
169 * Clears any previously stored user access
170 *
171 * @return bool
172 */
173 public function clear_user_access() {
174 return delete_option($this->access_key);
175 }
176
177 /**
178 * Saves user is and access token received from the auth server
179 *
180 * @param array $query Parameter array containing the user id and access token from the auth server.
181 * @return array $result An array containing a "success" or "failure" message as a result of the current process.
182 */
183 public function save_user_access($query) {
184
185 try {
186
187 $token = get_option($this->access_key, false);
188 $result = array();
189
190 if (false === $token) {
191 $token = array(
192 'user_id' => base64_decode(urldecode($query['user_id'])),
193 'access_token' => base64_decode(urldecode($query['access_token']))
194 );
195
196 if (false !== update_option($this->access_key, $token)) {
197 $result = array('error' => false, 'message' => 'ga_access_saved', 'values' => array());
198 } else {
199 $result = array('error' => true, 'message' => 'ga_access_saving_failed', 'values' => array($query['access_token']));
200 }
201 }
202
203 } catch (Exception $e) {
204 $result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
205 }
206
207 return $this->_response($result);
208 }
209
210 /**
211 * Saves the tracking code/id manually (user input)
212 *
213 * @param array $query Parameter array containing the tracking code/id to save.
214 * @return array $result An array containing the result of the process.
215 */
216 public function save_tracking_id($query) {
217 try {
218 $tracking_id = $query['tracking_id'];
219 $saved = false;
220
221 if (!empty($tracking_id)) {
222 $saved = set_transient($this->tracking_id_key, $tracking_id, $this->expiration);
223 }
224
225 $result = array('saved' => $saved);
226 } catch (Exception $e) {
227 $result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
228 }
229
230 return $this->_response($result);
231 }
232
233 /**
234 * Retrieves any available access token either previously saved info or
235 * from a new request from the Google Server.
236 *
237 * @internal
238 * @return string $authorization_code
239 */
240 private function _get_token() {
241
242 // Retrieves the tracking code/id if available
243 $tracking_id = $this->get_tracking_id();
244 $access_token = '';
245
246 $token = get_option($this->access_key, false);
247 if (false !== $token) {
248 $access_token = isset($token['access_token']) ? $token['access_token'] : '';
249 $user_id = isset($token['user_id']) ? $token['user_id'] : '';
250
251 if ((!empty($access_token) && !$this->_token_valid($access_token)) || (!empty($user_id) && empty($access_token) && !empty($tracking_id))) {
252 if (!empty($user_id)) {
253 $args = array(
254 'headers' => apply_filters('updraftplus_auth_headers', array())
255 );
256
257 $response = wp_remote_get($this->auth_endpoint.'?user_id='.$user_id.'&code=ud_googleanalytics_code', $args);
258 if (is_wp_error($response)) {
259 throw new Exception($response->get_error_message());
260 } else {
261 if (is_array($response)) {
262
263 $body = json_decode($response['body'], true);
264 $token_response = array();
265
266 if (is_array($body) && !isset($body['error'])) {
267 $token_response = json_decode(base64_decode($body[0]), true);
268 }
269
270 if (is_array($token_response) && isset($token_response['access_token'])) {
271 $access_token = $token_response['access_token'];
272 } else {
273 // If we don't get any valid response then that would mean that the
274 // permission was already revoked. Thus, we need to re-authorize the
275 // user before using the analytics feature once again.
276 $access_token = '';
277 }
278
279 $token['access_token'] = $access_token;
280 update_option($this->access_key, $token);
281 }
282 }
283 }
284 }
285 }
286
287 return $access_token;
288 }
289
290 /**
291 * Verifies whether the access token is still valid for use
292 *
293 * @internal
294 * @param string $token The access token to be check and validated
295 * @return bool
296 * @throws Exception If an error has occurred while connecting to the Google Server.
297 */
298 private function _token_valid($token) {
299
300 $response = wp_remote_get($this->token_info_endpoint.'?access_token='.$token);
301 if (is_wp_error($response)) {
302 throw new Exception($response->get_error_message());
303 } else {
304 if (is_array($response)) {
305 $response = json_decode($response['body'], true);
306 if (!empty($response)) {
307 if (!isset($response['error']) && !isset($response['error_description'])) {
308 return true;
309 }
310 }
311 }
312 }
313
314 return false;
315 }
316
317 /**
318 * Parses and extracts the google analytics information (NEEDED)
319 *
320 * @internal
321 * @param string $content The content to parse
322 * @return array An array containing the status of the process along with the tracking code/id
323 */
324 private function parse_content($content) {
325
326 $installed = false;
327 $gtm_installed = false;
328 $tracking_id = '';
329 $script_file_found = false;
330 $tracking_id_found = false;
331
332 // Pull google analytics script file(s)
333 preg_match_all('/<script\b[^>]*>([\s\S]*?)<\/script>/i', $content, $scripts);
334 for ($i=0; $i < count($scripts[0]); $i++) {
335 // Check for Google Analytics file
336 if (stristr($scripts[0][$i], 'ga.js') || stristr($scripts[0][$i], 'analytics.js')) {
337 $script_file_found = true;
338 }
339
340 // Check for Google Tag Manager file
341 // N.B. We are not checking for GTM but this check will be useful when
342 // showing the notice to the user if we haven't found Google Analytics
343 // directly being installed on the page.
344 if (stristr($scripts[0][$i], 'gtm.js')) {
345 $gtm_installed = true;
346 }
347 }
348
349 // Pull tracking code
350 preg_match_all('/UA-[0-9]{5,}-[0-9]{1,}/i', $content, $codes);
351 if (count($codes) > 0) {
352 if (!empty($codes[0])) {
353 $tracking_id_found = true;
354 $tracking_id = $codes[0][0];
355 }
356 }
357
358 // If we found both the script and the tracking code then it is safe
359 // to say that Google Analytics (GA) is installed. Thus, we're returning
360 // "true" as a response.
361 if ($script_file_found && $tracking_id_found) {
362 $installed = true;
363 }
364
365 // Return result of process.
366 return array(
367 'installed' => $installed,
368 'gtm_installed' => $gtm_installed,
369 'tracking_id' => $tracking_id
370 );
371 }
372
373 /**
374 * Retrieves the "analytics_tracking_id" transient
375 *
376 * @internal
377 * @return mixed Returns the value of the saved transient. Returns "false" if the transient does not exist.
378 */
379 private function get_tracking_id() {
380 return get_transient($this->tracking_id_key);
381 }
382
383
384 /**
385 * Returns the current tracking id
386 *
387 * @return array $result An array containing the Google Tracking ID.
388 */
389 public function get_current_tracking_id() {
390 try {
391
392 // Get current site transient stored for this key
393 $tracking_id = get_transient($this->tracking_id_key);
394
395 // Checks whether we have a valid token
396 $access_token = $this->_get_token();
397 if (empty($access_token)) {
398 $tracking_id = '';
399 }
400
401 if (false === $tracking_id) {
402 $result = $this->extract_tracking_id();
403 } else {
404 $result = array(
405 'installed' => true,
406 'tracking_id' => $tracking_id
407 );
408 }
409
410 } catch (Exception $e) {
411 $result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
412 }
413
414 return $this->_response($result);
415 }
416
417 /**
418 * Clears user access from database
419 *
420 * @return array $result An array containing the "Remove" confirmation whether the action succeeded or not.
421 */
422 public function remove_user_access() {
423 try {
424
425 // Clear user access
426 $is_cleared = $this->clear_user_access();
427
428 if (false !== $is_cleared) {
429 $result = array('removed' => true);
430 } else {
431 $result = array('error' => true, 'message' => 'user_access_remove_failed', 'values' => array());
432 }
433
434 } catch (Exception $e) {
435 $result = array('error' => true, 'message' => 'generic_response_error', 'values' => array($e->getMessage()));
436 }
437
438 return $this->_response($result);
439 }
440 }
441