/includes/admin/google.php

  1. <?php 
  2. /** 
  3. * Google Client admin class.  
  4. * 
  5. * Handles retrieving whether a particular notice has been dismissed or not,  
  6. * as well as marking a notice as dismissed. 
  7. * 
  8. * @since 6.0.0 
  9. * 
  10. * @package MonsterInsights 
  11. * @subpackage GA Client 
  12. * @author Chris Christoff 
  13. */ 
  14.  
  15. // Exit if accessed directly 
  16. if ( ! defined( 'ABSPATH' ) ) { 
  17. exit; 
  18.  
  19. final class MonsterInsights_GA { 
  20.  
  21. /** 
  22. * Holds the GA client object if using oAuth. 
  23. * 
  24. * @access public 
  25. * @since 6.0.0 
  26. * @var MonsterInsights_GA_Client $client GA client object. 
  27. */ 
  28. public $client; 
  29.  
  30. /** 
  31. * Google Profile ID. 
  32. * 
  33. * @access public 
  34. * @since 6.0.0 
  35. * @var int $profile ID of profile in use. 
  36. */ 
  37. public $profile; 
  38.  
  39. /** 
  40. * Google UA code. 
  41. * 
  42. * @access public 
  43. * @since 6.0.0 
  44. * @var string|false $ua Google UA code for the current profile if valid oAuth in use, else false. 
  45. */ 
  46. public $ua; 
  47.  
  48. /** 
  49. * Google profile name. 
  50. * 
  51. * @access public 
  52. * @since 6.0.0 
  53. * @var string|false $ua Google profile name for the current profile if valid oAuth in use, else false. 
  54. */ 
  55. public $name; 
  56.  
  57. /** 
  58. * Status of Google client object. 
  59. * 
  60. * @access public 
  61. * @since 6.0.0 
  62. * @var string $status Possible values include manual, expired, valid, needs-permissions, blocked and none. 
  63. */ 
  64. public $status; 
  65.  
  66. /** 
  67. * oAuth Permissions Version. 
  68. * 
  69. * @access public 
  70. * @since 6.0.0 
  71. * @var string $oauth_version Version of oAuth permissions granted. 
  72. */ 
  73. public $oauth_version;  
  74.  
  75. /** 
  76. * Holds the base object. 
  77. * 
  78. * @access public 
  79. * @since 6.0.0 
  80. * @var MonsterInsights $base MonsterInsights Base object. 
  81. */ 
  82. public $base; 
  83.  
  84. /** 
  85. * Primary class constructor. 
  86. * 
  87. * @access public 
  88. * @since 6.0.0 
  89. */ 
  90. public function __construct() { 
  91. // Get object  
  92. $this->client = $this->get_client(); 
  93. $this->profile = $this->get_profile(); 
  94. $this->ua = $this->get_ua(); 
  95. $this->name = $this->get_name(); 
  96. $this->oauth_version = $this->get_oauth_version(); 
  97. $this->status = $this->get_status(); 
  98. $this->base = MonsterInsights(); 
  99.  
  100.  
  101. // Show any info/error notices 
  102. $this->get_notices(); 
  103.  
  104. // Authentication Actions 
  105. add_action( 'wp_ajax_monsterinsights_google_view', array( $this, 'google_auth_view' ) ); 
  106. add_action( 'wp_ajax_monsterinsights_google_cancel', array( $this, 'google_cancel' ) ); 
  107.  
  108. add_action( 'admin_init', array( $this, 'deactivate_google' ) ); // Deactivate 
  109.  
  110. private function get_client() { 
  111. return ! empty( $this->client ) ? $this->client : monsterinsights_create_client(); 
  112.  
  113. private function set_test_client() {  
  114. $this->client = monsterinsights_create_test_client(); 
  115.  
  116. private function set_client() {  
  117. $this->client = monsterinsights_create_client(); 
  118.  
  119. /** 
  120. * Get the current GA profile 
  121. * 
  122. * @return null 
  123. */ 
  124. private function get_profile() { 
  125. return monsterinsights_get_option( 'analytics_profile', false ); 
  126.  
  127. private function get_name() { 
  128. return monsterinsights_get_option( 'analytics_profile_name', false ); 
  129.  
  130. private function get_ua() { 
  131. return monsterinsights_get_ua(); 
  132.  
  133. private function get_oauth_version() { 
  134. return monsterinsights_get_option( 'oauth_version', '1.0' ); 
  135.  
  136. private function get_status() { 
  137. $status = 'valid'; 
  138. if ( ! empty( $this->profile ) ) { 
  139. // We are using oAuth 
  140.  
  141. $last_run = monsterinsights_get_option( 'cron_last_run', false ); 
  142. $failed = monsterinsights_get_option( 'cron_failed', false ); 
  143. $dash_dis = monsterinsights_get_option( 'dashboards_disabled', false ); 
  144.  
  145. // See if issue connecting or expired 
  146. if ( ! $dash_dis && $failed && ( $last_run === false || monsterinsights_hours_between( $last_run ) >= 48 ) ) {  
  147. $status = 'blocked'; 
  148.  
  149. $access_token = $this->client->get_access_token(); 
  150.  
  151. // Check to make sure access token is there and not expired 
  152. if ( empty( $access_token ) || empty( $access_token['expires'] ) || current_time( 'timestamp' ) >= $access_token['expires'] ) { 
  153. $status = 'expired'; 
  154. return $status; 
  155.  
  156. // See if needs permissions 
  157. if ( version_compare( $this->oauth_version, '1.0', '<' ) ) {  
  158. $status = 'needs-permissions'; 
  159.  
  160. } else if ( ! empty( $this->ua ) ) { 
  161. // We are using manual 
  162.  
  163. } else { 
  164. // We do not have oAuth or manual active 
  165. $status = 'empty'; 
  166.  
  167. return $status; 
  168.  
  169. private function get_notices() { 
  170. // Notice for no manual or profile GA 
  171. if ( $this->status === 'empty' ) { 
  172. add_action( 'admin_notices', array( $this, 'monsterinsights_show_admin_config_empty_notice' ) ); 
  173.  
  174. $current_page = filter_input( INPUT_GET, 'page' ); 
  175.  
  176. // Only show expired, needs permission, or blocked notices on the MI pages 
  177. // We do this because unlike status empty, these only block reporting, not the frontend 
  178. // tracking from working, and as a result, these are not as urgent. Plus users generally 
  179. // don't like global notices. 
  180. if ( strpos( $current_page, 'monsterinsights' ) === 0 ) { 
  181.  
  182. // Notice for GA Access token expired (needs re-authenticate) 
  183. if ( $this->status === 'expired' ) { 
  184. add_action( 'admin_notices', array( $this, 'monsterinsights_show_admin_config_expired_notice' ) ); 
  185.  
  186. // Notice for Needs Permissions 
  187. if ( $this->status === 'needs-permissions' ) { 
  188. add_action( 'admin_notices', array( $this, 'monsterinsights_show_admin_config_needs_permissions_notice' ) ); 
  189.  
  190. // Notice for trouble connecting to Google 
  191. if ( $this->status === 'blocked' ) { 
  192. add_action( 'admin_notices', array( $this, 'monsterinsights_show_admin_config_blocked_notice' ) ); 
  193.  
  194. /** 
  195. * Used when switching GA profiles, or switching 
  196. * to/from oAuth <--> manual, or when reauthenticating 
  197. * or deleting GA profile. 
  198. * 
  199. * @return null 
  200. */ 
  201. private function reinitialize() {  
  202. // Get object  
  203. $this->client = $this->get_client(); 
  204. $this->profile = $this->get_profile(); 
  205. $this->ua = $this->get_ua(); 
  206. $this->name = $this->get_name(); 
  207. $this->oauth_version = $this->get_oauth_version(); 
  208. $this->status = $this->get_status(); 
  209.  
  210. // Re-get data if possible 
  211. if ( $this->ua === 'valid' ) { 
  212. $this->refresh_dashboard_data(); 
  213.  
  214. public function create_auth_url() { 
  215. return $this->client->createAuthUrl(); 
  216.  
  217. /** 
  218. * Getting the analytics profiles 
  219. * 
  220. * Doing the request to the Google analytics API and if there is a response, parses this response and return its 
  221. * array 
  222. * 
  223. * @return array 
  224. */ 
  225. public function get_profiles() { // @todo: this needs exception handling for a 401 login required  
  226. $accounts = $this->get_profiles_request(); 
  227. if ( is_array( $accounts ) ) { 
  228. return $accounts; 
  229. } else { 
  230. return array(); 
  231.  
  232. public function find_selected_profile( $profile_id ) { 
  233. $profiles = $this->get_profiles(); 
  234. $found = array(); 
  235. foreach ( $profiles as $account ) { 
  236. foreach ( $account['items'] as $profile ) { 
  237. foreach ( $profile['items'] as $subprofile ) { 
  238. if ( isset( $subprofile['id'] ) && $subprofile['id'] == $profile_id ) { 
  239. $found = array( 
  240. 'id' => $profile_id,  
  241. 'ua' => $subprofile['ua_code'],  
  242. 'name' => $subprofile['name'],  
  243.  
  244. ); 
  245. break 3; 
  246. return $found; 
  247.  
  248. public function save_selected_profile( $profile ) { 
  249. monsterinsights_update_option( 'analytics_profile', $profile['id'] ); 
  250. monsterinsights_update_option( 'analytics_profile_code', $profile['ua'] ); 
  251. monsterinsights_update_option( 'analytics_profile_name', $profile['name'] ); 
  252. monsterinsights_set_client_oauth_version(); 
  253.  
  254.  
  255. /** 
  256. * Get accounts request 
  257. * 
  258. * @param array $response 
  259. * 
  260. * @return mixed 
  261. */ 
  262. private function get_profiles_request() { 
  263. global $wp_version; 
  264. $version = str_replace( '-src', '', $wp_version ); 
  265. $accounts = array(); 
  266. $start_index = 1; 
  267. $paginate = false; 
  268. $continue = true; 
  269. while ( $continue ) { 
  270. $body = array( 
  271. 'max-results' => 1000,  
  272. 'start-index' => $paginate ? $start_index + 1000 : $start_index,  
  273. ); 
  274. if ( $paginate ) { 
  275. $start_index = $start_index + 1000; 
  276. $response = $this->client->do_request( 'https://www.googleapis.com/analytics/v3/management/accounts/~all/webproperties/~all/profiles', false, 'GET', $body ); 
  277. if ( ! empty( $response ) ) { 
  278. $response = array( 
  279. 'response' => array( 'code' => $this->client->get_http_response_code() ),  
  280. 'body' => json_decode( $response->getResponseBody(), true ),  
  281. ); 
  282. } else { 
  283. if ( version_compare( $version, '4.6', '<' ) ) { 
  284. return esc_html__( 'MonsterInsights requires WordPress version 4.6 or newer to use oAuth. Please update your WordPress version.', 'google-analytics-for-wordpress' ); 
  285. } else { 
  286. if ( ! empty( $accounts ) ) { 
  287. return $accounts; 
  288. } else { 
  289. return esc_html__( 'Google Analytics had a connection error or your Google account is not signed up for Google Analytics.', 'google-analytics-for-wordpress' ); 
  290.  
  291. if ( isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) { 
  292. if ( ! empty( $response['body']['items'] ) && is_array( $response['body']['items'] ) ) { 
  293. foreach ( $response['body']['items'] as $item ) { 
  294.  
  295. // Only deal with web properties, not apps. 
  296. if ( isset( $item['type'] ) && 'WEB' != $item['type'] ) { 
  297. continue; 
  298.  
  299. if ( empty( $accounts[ $item['accountId'] ] ) ) { 
  300. $accounts[ $item['accountId'] ] = array(  
  301. 'id' => $item['accountId'],  
  302. 'ua_code' => $item['webPropertyId'],  
  303. 'parent_name' => $item['websiteUrl'],  
  304. 'items' => array(),  
  305. ); 
  306.  
  307. if ( empty( $accounts[ $item['accountId'] ]['items'][ $item['internalWebPropertyId'] ] ) ) { 
  308. $accounts[ $item['accountId'] ]['items'][ $item['internalWebPropertyId'] ]= array(  
  309. 'id' => $item['webPropertyId'],  
  310. 'name' => $item['websiteUrl'],  
  311. 'items' => array(),  
  312. ); 
  313.  
  314. if ( empty( $accounts[ $item['accountId'] ]['items'][ $item['internalWebPropertyId'] ]['items'][ $item['id'] ] ) ) { 
  315. $accounts[ $item['accountId'] ]['items'][ $item['internalWebPropertyId'] ]['items'][ $item['id'] ] = array(  
  316. 'name' => $item['name'] . ' (' . $item['webPropertyId'] . ')',  
  317. 'ua_code' => $item['webPropertyId'],  
  318. 'id' => $item['id'],  
  319. ); 
  320. } else if ( isset( $response['response']['code'] ) && $response['response']['code'] !== 200 && ! $paginate ) { 
  321. if ( version_compare( $version, '4.6', '<' ) ) { 
  322. return esc_html__( 'MonsterInsights requires WordPress version 4.6 or newer to use oAuth. Please update your WordPress version.', 'google-analytics-for-wordpress' ); 
  323. } else { 
  324. if ( ! empty( $accounts ) ) { 
  325. return $accounts; 
  326. } else { 
  327. $code = isset( $response['response']['code'] ) ? $response['response']['code'] : 'Unknown'; 
  328. $type = isset( $response['body']['error']['errors'][0]['reason'] ) ? $response['body']['error']['errors'][0]['reason'] : false; 
  329. if ( $type === 'insufficientPermissions' ) { 
  330. return esc_html__( 'Please ensure your Google Account is signed up for Google Analytics.', 'google-analytics-for-wordpress' ); 
  331. } else if ( $type === 'badRequest' ) { 
  332. return esc_html__( 'Bad Request. Please contact support.', 'google-analytics-for-wordpress' ); 
  333. } else if ( $type === 'dailyLimitExceeded' ) { 
  334. return esc_html__( 'Daily Limit Exceeded. Please contact support.', 'google-analytics-for-wordpress' ); 
  335. } else if ( $type === 'userRateLimitExceeded' ) { 
  336. return esc_html__( 'User Rate Limit Exceeded. Wait 2 minutes and then try again.', 'google-analytics-for-wordpress' ); 
  337. } else if ( $type === 'rateLimitExceeded' ) { 
  338. return esc_html__( 'Project Rate Limit Exceeded. Wait 2 minutes and then try again.', 'google-analytics-for-wordpress' ); 
  339. } else if ( $type === 'quotaExceeded' ) { 
  340. return esc_html__( 'Project Rate Limit Quota Exceeded. Wait 2 minutes and then try again.', 'google-analytics-for-wordpress' ); 
  341. } else if ( $type === 'internalServerError' || $type === 'backendError' ) { 
  342. return esc_html__( 'Google Analytics is having API issues on their side. Wait 2 minutes and then try again.', 'google-analytics-for-wordpress' ); 
  343. } else { 
  344. return sprintf( esc_html__( 'Google Analytics had a connection error. Error code: %1$s. Reason: %2$s', 'google-analytics-for-wordpress' ), $code, $type ); 
  345.  
  346. if ( isset( $response['body']['totalResults'] ) && $start_index < $response['body']['totalResults'] && ! empty( $response['body']['nextLink'] ) ) { 
  347. $paginate = true; 
  348. } else { 
  349. $continue = false; 
  350. return $accounts; 
  351.  
  352. private function clear_oauth_data() { 
  353. // Delete the stored profiles 
  354. $options = array( 
  355. 'analytics_profile_code',  
  356. 'analytics_profile',  
  357. 'analytics_profile_name',  
  358. 'oauth_version',  
  359. 'cron_failed',  
  360. 'cron_last_run',  
  361. ); 
  362. monsterinsights_delete_options( $options ); 
  363.  
  364. // Destroy the data 
  365. $this->base->reports->delete_aggregate_data(); 
  366.  
  367. $this->client->clear_data(); 
  368.  
  369. private function clear_manual_data() { 
  370. // Delete the manual ua code 
  371. monsterinsights_delete_option( 'manual_ua_code' ); 
  372.  
  373. public function deactivate_google() { 
  374. // Check if user pressed the deactivate button and nonce is valid 
  375. if ( ! isset( $_POST['monsterinsights-google-deauthenticate-submit'] ) ) { 
  376. return; 
  377.  
  378. if ( ! wp_verify_nonce( $_POST['monsterinsights-google-authenticated-nonce'], 'monsterinsights-google-authenticated-nonce' ) ) { 
  379. return; 
  380.  
  381. if ( ! current_user_can( 'monsterinsights_save_settings' ) ) { 
  382. return; 
  383.  
  384. // Destroy the client 
  385. $this->clear_oauth_data(); 
  386.  
  387. // Refresh the client 
  388. $this->reinitialize(); 
  389.  
  390. public function refresh_dashboard_data( ) { 
  391. // Destroy the data 
  392. $this->base->reports->delete_aggregate_data(); 
  393.  
  394. $this->base->reports->run_cron(); 
  395.  
  396. /** 
  397. * Check if client has a refresh token 
  398. * @return bool 
  399. */ 
  400. public function has_refresh_token() { 
  401. return $this->client->is_authenticated(); 
  402.  
  403. /** 
  404. * Doing request to Google Analytics 
  405. * 
  406. * This method will do a request to google and get the response code and body from content 
  407. * 
  408. * @param string $target_request_url 
  409. * 
  410. * @return array|null 
  411. */ 
  412. public function do_request( $target_request_url ) { 
  413. $response = $this->client->do_request( $target_request_url ); 
  414. if ( ! empty( $response ) ) { 
  415. return array( 
  416. 'response' => array( 'code' => $this->client->get_http_response_code() ),  
  417. 'body' => json_decode( $response->getResponseBody(), true ),  
  418. ); 
  419.  
  420. /** 
  421. * Wrapper for authenticate the client. If authentication code is send it will get and check an access token. 
  422. * 
  423. * @param mixed $authentication_code 
  424. * 
  425. * @return boolean 
  426. */ 
  427. public function authenticate( $authentication_code = null ) { 
  428. // When authentication again we should clean up some stuff 
  429. monsterinsights_delete_options( array( 'cron_last_run', 'cron_failed' ) ); 
  430. return $this->client->authenticate_client( $authentication_code ); 
  431.  
  432. public function google_auth_view() { 
  433. $view = isset( $_POST['view'] ) && in_array( $_POST['view'], array( 'prestart', 'start', 'enterkey', 'selectprofile', 'done' ) ) ? $_POST['view'] : ''; 
  434. $reauth = isset( $_POST['reauth'] ) && $_POST['reauth'] && $_POST['reauth'] !== 'false' ? true : false; 
  435.  
  436. if ( ! current_user_can( 'monsterinsights_save_settings' ) ) { 
  437. echo esc_html__( 'Permission Denied', 'google-analytics-for-wordpress'); 
  438. wp_die(); 
  439.  
  440. // We run the save routines, if required, for a view, and then after that send back the results + next view 
  441. $nextview = array(); 
  442.  
  443. switch ( $view ) { 
  444. case 'start': 
  445. if ( ! $this->is_wp_blocking_google() && ! $this->is_google_on_blacklist() ) { 
  446. $auth_url = $this->create_auth_url(); 
  447. $nextview = monsterinsights_google_auth_enterkey_view( $reauth, $auth_url ); 
  448. } else { 
  449. $error = esc_html__( 'Cannot connect to Google', 'google-analytics-for-wordpress' ); 
  450. $nextview = monsterinsights_google_auth_error_view( $reauth, $error ); 
  451. break; 
  452.  
  453. case 'enterkey': 
  454. $auth_key = ! empty( $_POST['stepdata'] ) ? sanitize_text_field( $_POST['stepdata'] ) : ''; 
  455. if ( $auth_key ) { 
  456. if ( $this->test_authkey( $auth_key ) ) { 
  457. $profiles = $this->get_profiles_request(); 
  458. delete_option( 'monsterinsights_get_profiles' ); 
  459. update_option( 'monsterinsights_get_profiles', $profiles ); 
  460. if ( ! empty( $profiles ) && is_array( $profiles ) ) { 
  461. $select = $this->ga_select( $profiles ); 
  462. $nextview = monsterinsights_google_auth_selectprofile_view( $reauth, $select ); 
  463. } else if ( ! empty( $profiles ) && is_string( $profiles ) ) { 
  464. // Error from Google 
  465. $auth_url = $this->create_auth_url(); 
  466. $this->client->clear_data(); 
  467. $this->set_test_client(); 
  468. $nextview = monsterinsights_google_auth_enterkey_view( $reauth, $auth_url, esc_html( $profiles ) ); 
  469. } else { 
  470. // No profiles or not enough permissions 
  471. $auth_url = $this->create_auth_url(); 
  472. $this->client->clear_data(); 
  473. $this->set_test_client(); 
  474. $nextview = monsterinsights_google_auth_enterkey_view( $reauth, $auth_url, esc_html__( 'No profiles viewable for that account. Please use another account.', 'google-analytics-for-wordpress' ) ); 
  475. } else { 
  476. // if bad authentication error message 
  477. $auth_url = $this->create_auth_url(); 
  478. $this->client->clear_data(); 
  479. $this->set_test_client(); 
  480. $nextview = monsterinsights_google_auth_enterkey_view( $reauth, $auth_url, esc_html__( 'Bad Google Code. Please try again.', 'google-analytics-for-wordpress' ) ); 
  481. } else { 
  482. $auth_url = $this->create_auth_url(); 
  483. $this->client->clear_data(); 
  484. $this->set_test_client(); 
  485. // if no auth key error message 
  486. $nextview = monsterinsights_google_auth_enterkey_view( $reauth, $auth_url, esc_html__( 'Please paste in your Google code.', 'google-analytics-for-wordpress' ) ); 
  487. break; 
  488.  
  489. case 'selectprofile': 
  490. $profile = ! empty( $_POST['stepdata'] ) ? absint( sanitize_text_field( $_POST['stepdata'] ) ) : ''; 
  491. if ( ! empty( $profile ) ) { 
  492. $this->set_test_client(); 
  493. $profile = $this->find_selected_profile( $profile ); 
  494. if ( ! empty( $profile ) ) { 
  495.  
  496. $this->clear_manual_data(); // Just in case we were manual, clear out UA 
  497.  
  498. $this->client->move_test_to_live(); 
  499. $this->save_selected_profile( $profile ); 
  500. $this->set_client(); 
  501. $this->reinitialize(); 
  502.  
  503. // Refresh reporting data 
  504. $this->base->reports->refresh_aggregate_data(); 
  505.  
  506. $nextview = monsterinsights_google_auth_done_view( $reauth ); 
  507. } else { 
  508. // Invalid profile selected 
  509. $profiles = get_option( 'monsterinsights_get_profiles', array() ); 
  510. $select = $this->ga_select( $profiles ); 
  511. $nextview = monsterinsights_google_auth_selectprofile_view( $reauth, $select, esc_html__( 'Invalid profile selected.', 'google-analytics-for-wordpress' ) ); 
  512. } else {  
  513. // No profile selected 
  514. $profiles = get_option( 'monsterinsights_get_profiles', array() ); 
  515. $select = $this->ga_select( $profiles ); 
  516. $nextview = monsterinsights_google_auth_selectprofile_view( $reauth, $select, esc_html__( 'Please select a profile.', 'google-analytics-for-wordpress' ) ); 
  517. break; 
  518.  
  519. case 'done': 
  520. $nextview = monsterinsights_google_auth_done_view( $reauth ); 
  521. break; 
  522.  
  523. case 'prestart': 
  524. default: 
  525. $nextview = monsterinsights_google_auth_start_view( $reauth ); 
  526. break; 
  527. echo $nextview; 
  528. wp_die(); 
  529.  
  530. public function google_cancel() { 
  531. $view = isset( $_POST['view'] ) && in_array( $_POST['view'], array( 'prestart', 'start', 'enterkey', 'selectprofile', 'done' ) ) ? $_POST['view'] : ''; 
  532. $reauth = isset( $_POST['reauth'] ) && $_POST['reauth'] && $_POST['reauth'] !== 'false' ? true : false; 
  533.  
  534. if ( ! current_user_can( 'monsterinsights_save_settings' ) ) { 
  535. echo esc_html__( 'Permission Denied', 'google-analytics-for-wordpress'); 
  536. wp_die(); 
  537.  
  538. // If we cancelled on enterkey or selectprofile, delete the temp access tokens and set client back to normal client 
  539. if ( $view === 'enterkey' || $view === 'selectprofile' ) { 
  540. $this->client->clear_data(); 
  541. delete_option( 'monsterinsights_get_profiles' ); 
  542. $this->set_client(); 
  543.  
  544. private function test_authkey( $authkey ) { 
  545. $this->set_test_client(); 
  546. $result = $this->authenticate( $authkey ); 
  547. return $result; 
  548.  
  549. /** 
  550. * Generates the GA select box 
  551. * 
  552. * @return null|string 
  553. */ 
  554. private function ga_select( $profiles = array() ) { 
  555. if ( empty( $profiles ) || ! is_array( $profiles ) ) {  
  556. $profiles = $this->get_profiles(); 
  557.  
  558. $optgroups = array(); 
  559. foreach ( $profiles as $key => $value ) { 
  560. foreach ( $value['items'] as $subitem ) { 
  561. $optgroups[ $subitem['name'] ]['items'] = $subitem['items']; 
  562.  
  563. $values = $optgroups; 
  564. $select = ''; 
  565. $select .= '<div class="monsterinsights_ga_form">'; 
  566. $select .= '<label for="monsterinsights_step_data" id="monsterinsights_select_ga_profile_label">' . esc_html__( 'Analytics profile', 'google-analytics-for-wordpress' ) . ':</label>'; 
  567. $select .= '<select data-placeholder="' . esc_attr__( 'Select a profile', 'google-analytics-for-wordpress' ) . '" name="monsterinsights_step_data" class="monsterinsights-select300 monsterinsights_select_ga_profile" id="monsterinsights_step_data" style="width:80%;margin-left:10%;margin-right:10%;background-color: #FFF;">'; 
  568. $select .= '<option></option>'; 
  569.  
  570. if ( count( $values ) >= 1 ) { 
  571. foreach ( $values as $optgroup => $value ) { 
  572. if ( ! empty( $value['items'] ) ) { 
  573. $select .= $this->create_optgroup( $optgroup, $value ); 
  574. else { 
  575. $select .= '<option value="' . esc_attr( $value['id'] ) . '">' . esc_attr( stripslashes( $value['name'] ) ) . '</option>'; 
  576. $select .= '</select>'; 
  577. $select .= '</div>'; 
  578. return $select; 
  579.  
  580. /** 
  581. * Creates a optgroup with the items. If items contain items it will create a nested optgroup 
  582. * 
  583. * @param string $optgroup 
  584. * @param array $value 
  585. * @param array $select_value 
  586. * 
  587. * @return string 
  588. */ 
  589. private function create_optgroup( $optgroup, $value ) { 
  590. $optgroup = '<optgroup label="' . esc_attr( $optgroup ) . '" disabled>'; 
  591.  
  592. foreach ( $value['items'] as $option ) { 
  593. if ( ! empty( $option['items'] ) ) { 
  594.  
  595. $optgroup .= $this->create_optgroup( esc_attr( $option['name'] ), $option ); 
  596. else { 
  597. $optgroup .= '<option value="' . esc_attr( $option['id'] ) . '">' . esc_attr( stripslashes( $option['name'] ) ) . '</option>'; 
  598.  
  599. $optgroup .= '</optgroup>'; 
  600.  
  601. return $optgroup; 
  602.  
  603. /** 
  604. * See if Google is on the block_request list. 
  605. * 
  606. * @return bool  
  607. */ 
  608. private function is_wp_blocking_google() { 
  609. if ( defined( 'WP_HTTP_BLOCK_EXTERNAL' ) && WP_HTTP_BLOCK_EXTERNAL ) { // @todo: put this in sysinfo  
  610. if ( defined( 'WP_ACCESSIBLE_HOSTS' ) ) { 
  611. $on_blacklist = $this->is_google_on_blacklist(); 
  612. if ( $on_blacklist ) { 
  613. return true; 
  614. } else { 
  615. $params = array( 
  616. 'sslverify' => false,  
  617. 'timeout' => 60,  
  618. 'user-agent' => 'MonsterInsights/' . MONSTERINSIGHTS_VERSION,  
  619. 'body' => '' 
  620. ); 
  621. $response = wp_remote_get( 'https://www.googleapis.com/discovery/v1/apis/analytics/v3/rest', $params ); 
  622. if( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) { 
  623. return false; 
  624. } else { 
  625. return true; 
  626. } else { 
  627. return true; 
  628. } else { 
  629. $params = array( 
  630. 'sslverify' => false,  
  631. 'timeout' => 60,  
  632. 'user-agent' => 'MonsterInsights/' . MONSTERINSIGHTS_VERSION,  
  633. 'body' => '' 
  634. ); 
  635. $response = wp_remote_get( 'https://www.googleapis.com/discovery/v1/apis/analytics/v3/rest', $params ); 
  636.  
  637. if( !is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) { 
  638. return false; 
  639. } else { 
  640. return true; 
  641.  
  642. /** 
  643. * See if Google is on the block_request list. 
  644. * 
  645. * @return bool  
  646. */ 
  647. private function is_google_on_blacklist() { // @todo: put this in sysinfo 
  648. $wp_http = new WP_Http(); 
  649. if ( $wp_http->block_request( 'https://www.googleapis.com/discovery/v1/apis/analytics/v3/rest' ) ) { 
  650. return true; 
  651.  
  652. return false; 
  653.  
  654. public function monsterinsights_show_admin_config_empty_notice() { 
  655. $screen = get_current_screen();  
  656. if ( empty( $screen->id ) || strpos( $screen->id, 'monsterinsights' ) !== false ) { 
  657. return; 
  658. echo '<div class="error"><p>' .  
  659. sprintf( esc_html__( 'Please configure your %1$sGoogle Analytics settings%2$s!', 'google-analytics-for-wordpress' ),  
  660. '<a href="' . admin_url( 'admin.php?page=monsterinsights_settings' ) . '">',  
  661. '</a>' 
  662. . '</p></div>'; 
  663.  
  664. /** 
  665. * Throw a warning when the fetching failed 
  666. */ 
  667. public function monsterinsights_show_admin_config_expired_notice() { 
  668. echo '<div class="error"><p>' .  
  669. sprintf( 
  670. esc_html__( 'It seems the authentication for the plugin has expired or the connection to Google Analytics is blocked, please try %1$sre-authenticating%2$s with Google Analytics to allow the plugin to fetch data.', 'google-analytics-for-wordpress' ),  
  671. '<a href="' . admin_url( 'admin.php?page=monsterinsights_settings' ) . '">',  
  672. '</a>' 
  673. . '</p></div>'; 
  674.  
  675. /** 
  676. * Throw a warning when the fetching failed 
  677. */ 
  678. public function monsterinsights_show_admin_config_needs_permissions_notice() { 
  679. echo '<div class="error"><p>' .  
  680. sprintf( 
  681. esc_html__( 'It seems the authentication for the plugin is missing permissions. Please %1$sre-authenticate%2$s with Google Analytics to allow the plugin to fetch data.', 'google-analytics-for-wordpress' ),  
  682. '<a href="' . admin_url( 'admin.php?page=monsterinsights_settings' ) . '">',  
  683. '</a>' 
  684. . '</p></div>'; 
  685.  
  686. /** 
  687. * Throw a warning when the fetching failed 
  688. */ 
  689. public function monsterinsights_show_admin_config_blocked_notice() { 
  690. echo '<div class="error"><p>' .  
  691. sprintf( 
  692. esc_html__( 'Data is not up-to-date, there was an error in retrieving the data from Google Analytics. This error could be caused by several issues. If the error persists, please see %1$sthis page%2$s.', 'google-analytics-for-wordpress' ),  
  693. '<a href="https://www.monsterinsights.com/docs/blocked-connection/">',  
  694. '</a>' 
  695. . '</p></div>'; 
.