gapi.php 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961
  1. <?php
  2. /**
  3. * Author: ExactMetrics team
  4. * Author URI: https://exactmetrics.com
  5. * Copyright 2018 ExactMetrics team
  6. * License: GPLv2 or later
  7. * License URI: http://www.gnu.org/licenses/gpl-2.0.html
  8. */
  9. // Exit if accessed directly
  10. if ( ! defined( 'ABSPATH' ) )
  11. exit();
  12. if ( ! class_exists( 'GADWP_GAPI_Controller' ) ) {
  13. final class GADWP_GAPI_Controller {
  14. public $client;
  15. public $service;
  16. public $timeshift;
  17. public $managequota;
  18. private $gadwp;
  19. private $access = array( '65556128672.apps.googleusercontent.com', 'Kc7888wgbc_JbeCmApbFjnYpwE' );
  20. public function __construct() {
  21. $this->gadwp = GADWP();
  22. include_once ( GADWP_DIR . 'tools/src/Deconf/autoload.php' );
  23. $config = new Deconf_Config();
  24. $config->setCacheClass( 'Deconf_Cache_Null' );
  25. if ( function_exists( 'curl_version' ) ) {
  26. $curlversion = curl_version();
  27. $curl_options = array();
  28. if ( isset( $curlversion['version'] ) ) {
  29. $rightversion = ( version_compare( PHP_VERSION, '5.3.0' ) >= 0 ) && version_compare( $curlversion['version'], '7.10.8' ) >= 0;
  30. } else {
  31. $rightversion = false;
  32. }
  33. if ( $rightversion && defined( 'GADWP_IP_VERSION' ) && GADWP_IP_VERSION ) {
  34. $curl_options[CURLOPT_IPRESOLVE] = GADWP_IP_VERSION; // Force CURL_IPRESOLVE_V4 or CURL_IPRESOLVE_V6
  35. }
  36. // add Proxy server settings to curl, if defined
  37. if ( defined( 'WP_PROXY_HOST' ) && defined( 'WP_PROXY_PORT' ) ) {
  38. $curl_options[CURLOPT_PROXY] = WP_PROXY_HOST;
  39. $curl_options[CURLOPT_PROXYPORT] = WP_PROXY_PORT;
  40. }
  41. if ( defined( 'WP_PROXY_USERNAME' ) && defined( 'WP_PROXY_PASSWORD' ) ) {
  42. $curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
  43. $curl_options[CURLOPT_PROXYUSERPWD] = WP_PROXY_USERNAME . ':' . WP_PROXY_PASSWORD;
  44. }
  45. $curl_options = apply_filters( 'gadwp_curl_options', $curl_options );
  46. if ( ! empty( $curl_options ) ) {
  47. $config->setClassConfig( 'Deconf_IO_Curl', 'options', $curl_options );
  48. }
  49. }
  50. $this->client = new Deconf_Client( $config );
  51. $this->client->setScopes( array( 'https://www.googleapis.com/auth/analytics.readonly' ) );
  52. $this->client->setAccessType( 'offline' );
  53. $this->client->setApplicationName( 'GADWP ' . GADWP_CURRENT_VERSION );
  54. $this->client->setRedirectUri( 'urn:ietf:wg:oauth:2.0:oob' );
  55. $this->managequota = 'u' . get_current_user_id() . 's' . get_current_blog_id();
  56. $this->access = array_map( array( $this, 'map' ), $this->access );
  57. if ( $this->gadwp->config->options['user_api'] ) {
  58. $this->client->setClientId( $this->gadwp->config->options['client_id'] );
  59. $this->client->setClientSecret( $this->gadwp->config->options['client_secret'] );
  60. } else {
  61. $this->client->setClientId( $this->access[0] );
  62. $this->client->setClientSecret( $this->access[1] );
  63. }
  64. /**
  65. * GADWP Endpoint support
  66. */
  67. add_action( 'gadwp_endpoint_support', array( $this, 'add_endpoint_support' ) );
  68. $this->service = new Deconf_Service_Analytics( $this->client );
  69. if ( $this->gadwp->config->options['token'] ) {
  70. $token = $this->gadwp->config->options['token'];
  71. if ( $token ) {
  72. try {
  73. $this->client->setAccessToken( $token );
  74. if ( $this->client->isAccessTokenExpired() ) {
  75. $refreshtoken = $this->client->getRefreshToken();
  76. $this->client->refreshToken( $refreshtoken );
  77. }
  78. $this->gadwp->config->options['token'] = $this->client->getAccessToken();
  79. } catch ( Deconf_IO_Exception $e ) {
  80. $timeout = $this->get_timeouts( 'midnight' );
  81. GADWP_Tools::set_error( $e, $timeout );
  82. } catch ( Deconf_Service_Exception $e ) {
  83. $timeout = $this->get_timeouts( 'midnight' );
  84. GADWP_Tools::set_error( $e, $timeout );
  85. $this->reset_token();
  86. } catch ( Exception $e ) {
  87. $timeout = $this->get_timeouts( 'midnight' );
  88. GADWP_Tools::set_error( $e, $timeout );
  89. $this->reset_token();
  90. }
  91. if ( is_multisite() && $this->gadwp->config->options['network_mode'] ) {
  92. $this->gadwp->config->set_plugin_options( true );
  93. } else {
  94. $this->gadwp->config->set_plugin_options();
  95. }
  96. }
  97. }
  98. }
  99. public function add_endpoint_support( $request ) {
  100. if ( $this->gadwp->config->options['with_endpoint'] && ! $this->gadwp->config->options['user_api'] ) {
  101. $url = $request->getUrl();
  102. if ( in_array( $url, array( 'https://accounts.google.com/o/oauth2/token', 'https://accounts.google.com/o/oauth2/revoke' ) ) ) {
  103. if ( get_class( $this->client->getIo() ) != 'Deconf_IO_Stream' ) {
  104. $curl_old_options = $this->client->getClassConfig( 'Deconf_IO_Curl' );
  105. $curl_options = $curl_old_options['options'];
  106. $curl_options[CURLOPT_SSL_VERIFYPEER] = 0;
  107. $this->client->setClassConfig( 'Deconf_IO_Curl', 'options', $curl_options );
  108. } else {
  109. add_filter( 'gadwp_endpoint_stream_options', array( $this, 'add_endpoint_stream_ssl' ), 10 );
  110. }
  111. } else {
  112. if ( get_class( $this->client->getIo() ) != 'Deconf_IO_Stream' ) {
  113. $curl_old_options = $this->client->getClassConfig( 'Deconf_IO_Curl' );
  114. $curl_options = $curl_old_options['options'];
  115. if ( isset( $curl_options[CURLOPT_SSL_VERIFYPEER] ) ) {
  116. unset( $curl_options[CURLOPT_SSL_VERIFYPEER] );
  117. if ( empty( $curl_options ) ) {
  118. $this->client->setClassConfig( 'Deconf_IO_Curl', 'options', '' );
  119. } else {
  120. $this->client->setClassConfig( 'Deconf_IO_Curl', 'options', $curl_options );
  121. }
  122. }
  123. }
  124. }
  125. $url = str_replace( 'https://accounts.google.com/o/oauth2/token', GADWP_ENDPOINT_URL . 'gadwp-token.php', $url );
  126. $url = str_replace( 'https://accounts.google.com/o/oauth2/revoke', GADWP_ENDPOINT_URL . 'gadwp-revoke.php', $url );
  127. $request->setUrl( $url );
  128. if ( ! $request->getUserAgent() ) {
  129. $request->setUserAgent( $this->client->getApplicationName() );
  130. }
  131. }
  132. }
  133. public function add_endpoint_stream_ssl( $requestSslContext ) {
  134. return array( "verify_peer" => false );
  135. }
  136. /**
  137. * Handles errors returned by GAPI Library
  138. *
  139. * @return boolean
  140. */
  141. public function gapi_errors_handler() {
  142. $errors = GADWP_Tools::get_cache( 'gapi_errors' );
  143. if ( false === $errors || ! isset( $errors[0] ) ) { // invalid error
  144. return false;
  145. }
  146. if ( isset( $errors[1][0]['reason'] ) && ( 'invalidParameter' == $errors[1][0]['reason'] || 'badRequest' == $errors[1][0]['reason'] || 'invalidCredentials' == $errors[1][0]['reason'] || 'insufficientPermissions' == $errors[1][0]['reason'] || 'required' == $errors[1][0]['reason'] ) ) {
  147. $this->reset_token();
  148. return true;
  149. }
  150. /** Back-off system for subsequent requests - an Auth error generated after a Service request
  151. * The native back-off system for Service requests is covered by the GAPI PHP Client
  152. */
  153. if ( isset( $errors[1][0]['reason'] ) && ( 'authError' == $errors[1][0]['reason'] ) ) {
  154. if ( $this->gadwp->config->options['api_backoff'] <= 5 ) {
  155. usleep( $this->gadwp->config->options['api_backoff'] * 1000000 + rand( 100000, 1000000 ) );
  156. $this->gadwp->config->options['api_backoff'] = $this->gadwp->config->options['api_backoff'] + 1;
  157. $this->gadwp->config->set_plugin_options();
  158. return false;
  159. } else {
  160. return true;
  161. }
  162. }
  163. if ( 500 == $errors[0] || 503 == $errors[0] || 400 == $errors[0] || 401 == $errors[0] || 403 == $errors[0] || $errors[0] < - 50 ) {
  164. return true;
  165. }
  166. return false;
  167. }
  168. /**
  169. * Calculates proper timeouts for each GAPI query
  170. *
  171. * @param
  172. * $interval
  173. * @return number
  174. */
  175. public function get_timeouts( $interval = '' ) {
  176. $local_time = time() + $this->timeshift;
  177. if ( 'daily' == $interval ) {
  178. $nextday = explode( '-', date( 'n-j-Y', strtotime( ' +1 day', $local_time ) ) );
  179. $midnight = mktime( 0, 0, 0, $nextday[0], $nextday[1], $nextday[2] );
  180. return $midnight - $local_time;
  181. } else if ( 'midnight' == $interval ) {
  182. $midnight = strtotime( "tomorrow 00:00:00" ); // UTC midnight
  183. $midnight = $midnight + 8 * 3600; // UTC 8 AM
  184. return $midnight - time();
  185. } else if ( 'hourly' == $interval ) {
  186. $nexthour = explode( '-', date( 'H-n-j-Y', strtotime( ' +1 hour', $local_time ) ) );
  187. $newhour = mktime( $nexthour[0], 0, 0, $nexthour[1], $nexthour[2], $nexthour[3] );
  188. return $newhour - $local_time;
  189. } else {
  190. $newtime = strtotime( ' +5 minutes', $local_time );
  191. return $newtime - $local_time;
  192. }
  193. }
  194. /**
  195. * Generates and retrieves the Access Code
  196. */
  197. public function token_request() {
  198. $data['authUrl'] = $this->client->createAuthUrl();
  199. GADWP_Tools::load_view( 'admin/views/access-code.php', $data );
  200. }
  201. /**
  202. * Retrieves all Google Analytics Views with details
  203. *
  204. * @return array
  205. */
  206. public function refresh_profiles() {
  207. try {
  208. $ga_profiles_list = array();
  209. $startindex = 1;
  210. $totalresults = 65535; // use something big
  211. while ( $startindex < $totalresults ) {
  212. $profiles = $this->service->management_profiles->listManagementProfiles( '~all', '~all', array( 'start-index' => $startindex ) );
  213. $items = $profiles->getItems();
  214. $totalresults = $profiles->getTotalResults();
  215. if ( $totalresults > 0 ) {
  216. foreach ( $items as $profile ) {
  217. $timetz = new DateTimeZone( $profile->getTimezone() );
  218. $localtime = new DateTime( 'now', $timetz );
  219. $timeshift = strtotime( $localtime->format( 'Y-m-d H:i:s' ) ) - time();
  220. $ga_profiles_list[] = array( $profile->getName(), $profile->getId(), $profile->getwebPropertyId(), $profile->getwebsiteUrl(), $timeshift, $profile->getTimezone(), $profile->getDefaultPage() );
  221. $startindex++;
  222. }
  223. }
  224. }
  225. if ( empty( $ga_profiles_list ) ) {
  226. $timeout = $this->get_timeouts( 'midnight' );
  227. GADWP_Tools::set_error( 'No properties were found in this account!', $timeout );
  228. } else {
  229. GADWP_Tools::delete_cache( 'last_error' );
  230. }
  231. return $ga_profiles_list;
  232. } catch ( Deconf_IO_Exception $e ) {
  233. $timeout = $this->get_timeouts( 'midnight' );
  234. GADWP_Tools::set_error( $e, $timeout );
  235. return $ga_profiles_list;
  236. } catch ( Deconf_Service_Exception $e ) {
  237. $timeout = $this->get_timeouts( 'midnight' );
  238. GADWP_Tools::set_error( $e, $timeout );
  239. } catch ( Exception $e ) {
  240. $timeout = $this->get_timeouts( 'midnight' );
  241. GADWP_Tools::set_error( $e, $timeout );
  242. }
  243. }
  244. /**
  245. * Handles the token reset process
  246. *
  247. * @param
  248. * $all
  249. */
  250. public function reset_token( $all = false ) {
  251. $this->gadwp->config->options['token'] = "";
  252. if ( $all ) {
  253. $this->gadwp->config->options['tableid_jail'] = "";
  254. $this->gadwp->config->options['ga_profiles_list'] = array();
  255. try {
  256. $this->client->revokeToken();
  257. } catch ( Exception $e ) {
  258. if ( is_multisite() && $this->gadwp->config->options['network_mode'] ) {
  259. $this->gadwp->config->set_plugin_options( true );
  260. } else {
  261. $this->gadwp->config->set_plugin_options();
  262. }
  263. }
  264. }
  265. if ( is_multisite() && $this->gadwp->config->options['network_mode'] ) {
  266. $this->gadwp->config->set_plugin_options( true );
  267. } else {
  268. $this->gadwp->config->set_plugin_options();
  269. }
  270. }
  271. /**
  272. * Get and cache Core Reports
  273. *
  274. * @param
  275. * $projecId
  276. * @param
  277. * $from
  278. * @param
  279. * $to
  280. * @param
  281. * $metrics
  282. * @param
  283. * $options
  284. * @param
  285. * $serial
  286. * @return int|Deconf_Service_Analytics_GaData
  287. */
  288. private function handle_corereports( $projectId, $from, $to, $metrics, $options, $serial ) {
  289. try {
  290. if ( 'today' == $from ) {
  291. $interval = 'hourly';
  292. } else {
  293. $interval = 'daily';
  294. }
  295. $transient = GADWP_Tools::get_cache( $serial );
  296. if ( false === $transient ) {
  297. if ( $this->gapi_errors_handler() ) {
  298. return - 23;
  299. }
  300. $options['samplingLevel'] = 'HIGHER_PRECISION';
  301. $data = $this->service->data_ga->get( 'ga:' . $projectId, $from, $to, $metrics, $options );
  302. if ( method_exists( $data, 'getContainsSampledData' ) && $data->getContainsSampledData() ) {
  303. $sampling['date'] = date( 'Y-m-d H:i:s' );
  304. $sampling['percent'] = number_format( ( $data->getSampleSize() / $data->getSampleSpace() ) * 100, 2 ) . '%';
  305. $sampling['sessions'] = $data->getSampleSize() . ' / ' . $data->getSampleSpace();
  306. GADWP_Tools::set_cache( 'sampleddata', $sampling, 30 * 24 * 3600 );
  307. GADWP_Tools::set_cache( $serial, $data, $this->get_timeouts( 'hourly' ) ); // refresh every hour if data is sampled
  308. } else {
  309. GADWP_Tools::set_cache( $serial, $data, $this->get_timeouts( $interval ) );
  310. }
  311. } else {
  312. $data = $transient;
  313. }
  314. } catch ( Deconf_Service_Exception $e ) {
  315. $timeout = $this->get_timeouts( 'midnight' );
  316. GADWP_Tools::set_error( $e, $timeout );
  317. return $e->getCode();
  318. } catch ( Exception $e ) {
  319. $timeout = $this->get_timeouts( 'midnight' );
  320. GADWP_Tools::set_error( $e, $timeout );
  321. return $e->getCode();
  322. }
  323. $this->gadwp->config->options['api_backoff'] = 0;
  324. $this->gadwp->config->set_plugin_options();
  325. if ( $data->getRows() > 0 ) {
  326. return $data;
  327. } else {
  328. $data->rows = array();
  329. return $data;
  330. }
  331. }
  332. /**
  333. * Generates serials for transients
  334. *
  335. * @param
  336. * $serial
  337. * @return string
  338. */
  339. public function get_serial( $serial ) {
  340. return sprintf( "%u", crc32( $serial ) );
  341. }
  342. /**
  343. * Analytics data for Area Charts (Admin Dashboard Widget report)
  344. *
  345. * @param
  346. * $projectId
  347. * @param
  348. * $from
  349. * @param
  350. * $to
  351. * @param
  352. * $query
  353. * @param
  354. * $filter
  355. * @return array|int
  356. */
  357. private function get_areachart_data( $projectId, $from, $to, $query, $filter = '' ) {
  358. switch ( $query ) {
  359. case 'users' :
  360. $title = __( "Users", 'google-analytics-dashboard-for-wp' );
  361. break;
  362. case 'pageviews' :
  363. $title = __( "Page Views", 'google-analytics-dashboard-for-wp' );
  364. break;
  365. case 'visitBounceRate' :
  366. $title = __( "Bounce Rate", 'google-analytics-dashboard-for-wp' );
  367. break;
  368. case 'organicSearches' :
  369. $title = __( "Organic Searches", 'google-analytics-dashboard-for-wp' );
  370. break;
  371. case 'uniquePageviews' :
  372. $title = __( "Unique Page Views", 'google-analytics-dashboard-for-wp' );
  373. break;
  374. default :
  375. $title = __( "Sessions", 'google-analytics-dashboard-for-wp' );
  376. }
  377. $metrics = 'ga:' . $query;
  378. if ( 'today' == $from || 'yesterday' == $from ) {
  379. $dimensions = 'ga:hour';
  380. $dayorhour = __( "Hour", 'google-analytics-dashboard-for-wp' );
  381. } else if ( '365daysAgo' == $from || '1095daysAgo' == $from ) {
  382. $dimensions = 'ga:yearMonth, ga:month';
  383. $dayorhour = __( "Date", 'google-analytics-dashboard-for-wp' );
  384. } else {
  385. $dimensions = 'ga:date,ga:dayOfWeekName';
  386. $dayorhour = __( "Date", 'google-analytics-dashboard-for-wp' );
  387. }
  388. $options = array( 'dimensions' => $dimensions, 'quotaUser' => $this->managequota . 'p' . $projectId );
  389. if ( $filter ) {
  390. $options['filters'] = 'ga:pagePath==' . $filter;
  391. }
  392. $serial = 'qr2_' . $this->get_serial( $projectId . $from . $metrics . $filter );
  393. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  394. if ( is_numeric( $data ) ) {
  395. return $data;
  396. }
  397. if ( empty( $data->rows ) ) {
  398. // unable to render it as an Area Chart, returns a numeric value to be handled by reportsx.js
  399. return - 21;
  400. }
  401. $gadwp_data = array( array( $dayorhour, $title ) );
  402. if ( 'today' == $from || 'yesterday' == $from ) {
  403. foreach ( $data->getRows() as $row ) {
  404. $gadwp_data[] = array( (int) $row[0] . ':00', round( $row[1], 2 ) );
  405. }
  406. } else if ( '365daysAgo' == $from || '1095daysAgo' == $from ) {
  407. foreach ( $data->getRows() as $row ) {
  408. /*
  409. * translators:
  410. * Example: 'F, Y' will become 'November, 2015'
  411. * For details see: http://php.net/manual/en/function.date.php#refsect1-function.date-parameters
  412. */
  413. $gadwp_data[] = array( date_i18n( __( 'F, Y', 'google-analytics-dashboard-for-wp' ), strtotime( $row[0] . '01' ) ), round( $row[2], 2 ) );
  414. }
  415. } else {
  416. foreach ( $data->getRows() as $row ) {
  417. /*
  418. * translators:
  419. * Example: 'l, F j, Y' will become 'Thusday, November 17, 2015'
  420. * For details see: http://php.net/manual/en/function.date.php#refsect1-function.date-parameters
  421. */
  422. $gadwp_data[] = array( date_i18n( __( 'l, F j, Y', 'google-analytics-dashboard-for-wp' ), strtotime( $row[0] ) ), round( $row[2], 2 ) );
  423. }
  424. }
  425. return $gadwp_data;
  426. }
  427. /**
  428. * Analytics data for Bottom Stats (bottom stats on main report)
  429. *
  430. * @param
  431. * $projectId
  432. * @param
  433. * $from
  434. * @param
  435. * $to
  436. * @param
  437. * $filter
  438. * @return array|int
  439. */
  440. private function get_bottomstats( $projectId, $from, $to, $filter = '' ) {
  441. $options = array( 'dimensions' => null, 'quotaUser' => $this->managequota . 'p' . $projectId );
  442. if ( $filter ) {
  443. $options['filters'] = 'ga:pagePath==' . $filter;
  444. $metrics = 'ga:uniquePageviews,ga:users,ga:pageviews,ga:BounceRate,ga:organicSearches,ga:pageviewsPerSession,ga:avgTimeOnPage,ga:avgPageLoadTime,ga:exitRate';
  445. } else {
  446. $metrics = 'ga:sessions,ga:users,ga:pageviews,ga:BounceRate,ga:organicSearches,ga:pageviewsPerSession,ga:avgTimeOnPage,ga:avgPageLoadTime,ga:avgSessionDuration';
  447. }
  448. $serial = 'qr3_' . $this->get_serial( $projectId . $from . $filter );
  449. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  450. if ( is_numeric( $data ) ) {
  451. return $data;
  452. }
  453. $gadwp_data = array();
  454. foreach ( $data->getRows() as $row ) {
  455. $gadwp_data = array_map( 'floatval', $row );
  456. }
  457. // i18n support
  458. $gadwp_data[0] = isset( $gadwp_data[0] ) ? number_format_i18n( $gadwp_data[0] ) : 0;
  459. $gadwp_data[1] = isset( $gadwp_data[1] ) ? number_format_i18n( $gadwp_data[1] ) : 0;
  460. $gadwp_data[2] = isset( $gadwp_data[2] ) ? number_format_i18n( $gadwp_data[2] ) : 0;
  461. $gadwp_data[3] = isset( $gadwp_data[3] ) ? number_format_i18n( $gadwp_data[3], 2 ) . '%' : '0%';
  462. $gadwp_data[4] = isset( $gadwp_data[4] ) ? number_format_i18n( $gadwp_data[4] ) : 0;
  463. $gadwp_data[5] = isset( $gadwp_data[5] ) ? number_format_i18n( $gadwp_data[5], 2 ) : 0;
  464. $gadwp_data[6] = isset( $gadwp_data[6] ) ? gmdate( "H:i:s", $gadwp_data[6] ) : '00:00:00';
  465. $gadwp_data[7] = isset( $gadwp_data[7] ) ? number_format_i18n( $gadwp_data[7], 2 ) : 0;
  466. if ( $filter ) {
  467. $gadwp_data[8] = isset( $gadwp_data[8] ) ? number_format_i18n( $gadwp_data[8], 2 ) . '%' : '0%';
  468. } else {
  469. $gadwp_data[8] = isset( $gadwp_data[8] ) ? gmdate( "H:i:s", $gadwp_data[8] ) : '00:00:00';
  470. }
  471. return $gadwp_data;
  472. }
  473. /**
  474. * Analytics data for Table Charts (content pages)
  475. *
  476. * @param
  477. * $projectId
  478. * @param
  479. * $from
  480. * @param
  481. * $to
  482. * @param
  483. * $filter
  484. * @return array|int
  485. */
  486. private function get_contentpages( $projectId, $from, $to, $filter = '', $metric ) {
  487. $metrics = 'ga:' . $metric;
  488. $dimensions = 'ga:pageTitle';
  489. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  490. if ( $filter ) {
  491. $options['filters'] = 'ga:pagePath==' . $filter;
  492. }
  493. $serial = 'qr4_' . $this->get_serial( $projectId . $from . $filter . $metric );
  494. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  495. if ( is_numeric( $data ) ) {
  496. return $data;
  497. }
  498. $gadwp_data = array( array( __( "Pages", 'google-analytics-dashboard-for-wp' ), __( ucfirst( $metric ), 'google-analytics-dashboard-for-wp' ) ) );
  499. foreach ( $data->getRows() as $row ) {
  500. $gadwp_data[] = array( esc_html( $row[0] ), (int) $row[1] );
  501. }
  502. return $gadwp_data;
  503. }
  504. /**
  505. * Analytics data for 404 Errors
  506. *
  507. * @param
  508. * $projectId
  509. * @param
  510. * $from
  511. * @param
  512. * $to
  513. * @return array|int
  514. */
  515. private function get_404errors( $projectId, $from, $to, $filter = "Page Not Found", $metric ) {
  516. $metrics = 'ga:' . $metric;
  517. $dimensions = 'ga:pagePath,ga:fullReferrer';
  518. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  519. $options['filters'] = 'ga:pageTitle=@' . $filter;
  520. $serial = 'qr4_' . $this->get_serial( $projectId . $from . $filter . $metric );
  521. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  522. if ( is_numeric( $data ) ) {
  523. return $data;
  524. }
  525. $gadwp_data = array( array( __( "404 Errors", 'google-analytics-dashboard-for-wp' ), __( ucfirst( $metric ), 'google-analytics-dashboard-for-wp' ) ) );
  526. foreach ( $data->getRows() as $row ) {
  527. $path = esc_html( $row[0] );
  528. $source = esc_html( $row[1] );
  529. $gadwp_data[] = array( "<strong>" . __( "URI:", 'google-analytics-dashboard-for-wp' ) . "</strong> " . $path . "<br><strong>" . __( "Source:", 'google-analytics-dashboard-for-wp' ) . "</strong> " . $source, (int) $row[2] );
  530. }
  531. return $gadwp_data;
  532. }
  533. /**
  534. * Analytics data for Table Charts (referrers)
  535. *
  536. * @param
  537. * $projectId
  538. * @param
  539. * $from
  540. * @param
  541. * $to
  542. * @param
  543. * $filter
  544. * @return array|int
  545. */
  546. private function get_referrers( $projectId, $from, $to, $filter = '', $metric ) {
  547. $metrics = 'ga:' . $metric;
  548. $dimensions = 'ga:source';
  549. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  550. if ( $filter ) {
  551. $options['filters'] = 'ga:medium==referral;ga:pagePath==' . $filter;
  552. } else {
  553. $options['filters'] = 'ga:medium==referral';
  554. }
  555. $serial = 'qr5_' . $this->get_serial( $projectId . $from . $filter . $metric );
  556. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  557. if ( is_numeric( $data ) ) {
  558. return $data;
  559. }
  560. $gadwp_data = array( array( __( "Referrers", 'google-analytics-dashboard-for-wp' ), __( ucfirst( $metric ), 'google-analytics-dashboard-for-wp' ) ) );
  561. foreach ( $data->getRows() as $row ) {
  562. $gadwp_data[] = array( esc_html( $row[0] ), (int) $row[1] );
  563. }
  564. return $gadwp_data;
  565. }
  566. /**
  567. * Analytics data for Table Charts (searches)
  568. *
  569. * @param
  570. * $projectId
  571. * @param
  572. * $from
  573. * @param
  574. * $to
  575. * @param
  576. * $filter
  577. * @return array|int
  578. */
  579. private function get_searches( $projectId, $from, $to, $filter = '', $metric ) {
  580. $metrics = 'ga:' . $metric;
  581. $dimensions = 'ga:keyword';
  582. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  583. if ( $filter ) {
  584. $options['filters'] = 'ga:keyword!=(not set);ga:pagePath==' . $filter;
  585. } else {
  586. $options['filters'] = 'ga:keyword!=(not set)';
  587. }
  588. $serial = 'qr6_' . $this->get_serial( $projectId . $from . $filter . $metric );
  589. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  590. if ( is_numeric( $data ) ) {
  591. return $data;
  592. }
  593. $gadwp_data = array( array( __( "Searches", 'google-analytics-dashboard-for-wp' ), __( ucfirst( $metric ), 'google-analytics-dashboard-for-wp' ) ) );
  594. foreach ( $data->getRows() as $row ) {
  595. $gadwp_data[] = array( esc_html( $row[0] ), (int) $row[1] );
  596. }
  597. return $gadwp_data;
  598. }
  599. /**
  600. * Analytics data for Table Charts (location reports)
  601. *
  602. * @param
  603. * $projectId
  604. * @param
  605. * $from
  606. * @param
  607. * $to
  608. * @param
  609. * $filter
  610. * @return array|int
  611. */
  612. private function get_locations( $projectId, $from, $to, $filter = '', $metric ) {
  613. $metrics = 'ga:' . $metric;
  614. $options = "";
  615. $title = __( "Countries", 'google-analytics-dashboard-for-wp' );
  616. $serial = 'qr7_' . $this->get_serial( $projectId . $from . $filter . $metric );
  617. $dimensions = 'ga:country';
  618. $local_filter = '';
  619. if ( $this->gadwp->config->options['ga_target_geomap'] ) {
  620. $dimensions = 'ga:city, ga:region';
  621. $country_codes = GADWP_Tools::get_countrycodes();
  622. if ( isset( $country_codes[$this->gadwp->config->options['ga_target_geomap']] ) ) {
  623. $local_filter = 'ga:country==' . ( $country_codes[$this->gadwp->config->options['ga_target_geomap']] );
  624. $title = __( "Cities from", 'google-analytics-dashboard-for-wp' ) . ' ' . __( $country_codes[$this->gadwp->config->options['ga_target_geomap']] );
  625. $serial = 'qr7_' . $this->get_serial( $projectId . $from . $this->gadwp->config->options['ga_target_geomap'] . $filter . $metric );
  626. }
  627. }
  628. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  629. if ( $filter ) {
  630. $options['filters'] = 'ga:pagePath==' . $filter;
  631. if ( $local_filter ) {
  632. $options['filters'] .= ';' . $local_filter;
  633. }
  634. } else {
  635. if ( $local_filter ) {
  636. $options['filters'] = $local_filter;
  637. }
  638. }
  639. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  640. if ( is_numeric( $data ) ) {
  641. return $data;
  642. }
  643. $gadwp_data = array( array( $title, __( ucfirst( $metric ), 'google-analytics-dashboard-for-wp' ) ) );
  644. foreach ( $data->getRows() as $row ) {
  645. if ( isset( $row[2] ) ) {
  646. $gadwp_data[] = array( esc_html( $row[0] ) . ', ' . esc_html( $row[1] ), (int) $row[2] );
  647. } else {
  648. $gadwp_data[] = array( esc_html( $row[0] ), (int) $row[1] );
  649. }
  650. }
  651. return $gadwp_data;
  652. }
  653. /**
  654. * Analytics data for Org Charts (traffic channels, device categories)
  655. *
  656. * @param
  657. * $projectId
  658. * @param
  659. * $from
  660. * @param
  661. * $to
  662. * @param
  663. * $query
  664. * @param
  665. * $filter
  666. * @return array|int
  667. */
  668. private function get_orgchart_data( $projectId, $from, $to, $query, $filter = '', $metric ) {
  669. $metrics = 'ga:' . $metric;
  670. $dimensions = 'ga:' . $query;
  671. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  672. if ( $filter ) {
  673. $options['filters'] = 'ga:pagePath==' . $filter;
  674. }
  675. $serial = 'qr8_' . $this->get_serial( $projectId . $from . $query . $filter . $metric );
  676. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  677. if ( is_numeric( $data ) ) {
  678. return $data;
  679. }
  680. if ( empty( $data->rows ) ) {
  681. // unable to render as an Org Chart, returns a numeric value to be handled by reportsx.js
  682. return - 21;
  683. }
  684. $block = ( 'channelGrouping' == $query ) ? __( "Channels", 'google-analytics-dashboard-for-wp' ) : __( "Devices", 'google-analytics-dashboard-for-wp' );
  685. $gadwp_data = array( array( '<div style="color:black; font-size:1.1em">' . $block . '</div><div style="color:darkblue; font-size:1.2em">' . (int) $data['totalsForAllResults'][$metrics] . '</div>', "" ) );
  686. foreach ( $data->getRows() as $row ) {
  687. $shrink = explode( " ", $row[0] );
  688. $gadwp_data[] = array( '<div style="color:black; font-size:1.1em">' . esc_html( $shrink[0] ) . '</div><div style="color:darkblue; font-size:1.2em">' . (int) $row[1] . '</div>', '<div style="color:black; font-size:1.1em">' . $block . '</div><div style="color:darkblue; font-size:1.2em">' . (int) $data['totalsForAllResults'][$metrics] . '</div>' );
  689. }
  690. return $gadwp_data;
  691. }
  692. /**
  693. * Analytics data for Pie Charts (traffic mediums, serach engines, social networks, browsers, screen rsolutions, etc.)
  694. *
  695. * @param
  696. * $projectId
  697. * @param
  698. * $from
  699. * @param
  700. * $to
  701. * @param
  702. * $query
  703. * @param
  704. * $filter
  705. * @return array|int
  706. */
  707. private function get_piechart_data( $projectId, $from, $to, $query, $filter = '', $metric ) {
  708. $metrics = 'ga:' . $metric;
  709. $dimensions = 'ga:' . $query;
  710. if ( 'source' == $query ) {
  711. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  712. if ( $filter ) {
  713. $options['filters'] = 'ga:medium==organic;ga:keyword!=(not set);ga:pagePath==' . $filter;
  714. } else {
  715. $options['filters'] = 'ga:medium==organic;ga:keyword!=(not set)';
  716. }
  717. } else {
  718. $options = array( 'dimensions' => $dimensions, 'sort' => '-' . $metrics, 'quotaUser' => $this->managequota . 'p' . $projectId );
  719. if ( $filter ) {
  720. $options['filters'] = 'ga:' . $query . '!=(not set);ga:pagePath==' . $filter;
  721. } else {
  722. $options['filters'] = 'ga:' . $query . '!=(not set)';
  723. }
  724. }
  725. $serial = 'qr10_' . $this->get_serial( $projectId . $from . $query . $filter . $metric );
  726. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  727. if ( is_numeric( $data ) ) {
  728. return $data;
  729. }
  730. $gadwp_data = array( array( __( "Type", 'google-analytics-dashboard-for-wp' ), __( ucfirst( $metric ), 'google-analytics-dashboard-for-wp' ) ) );
  731. $i = 0;
  732. $included = 0;
  733. foreach ( $data->getRows() as $row ) {
  734. if ( $i < 20 ) {
  735. $gadwp_data[] = array( str_replace( "(none)", "direct", esc_html( $row[0] ) ), (int) $row[1] );
  736. $included += $row[1];
  737. $i++;
  738. } else {
  739. break;
  740. }
  741. }
  742. $totals = $data->getTotalsForAllResults();
  743. $others = $totals[$metrics] - $included;
  744. if ( $others > 0 ) {
  745. $gadwp_data[] = array( __( 'Other', 'google-analytics-dashboard-for-wp' ), $others );
  746. }
  747. return $gadwp_data;
  748. }
  749. /**
  750. * Analytics data for Frontend Widget (chart data and totals)
  751. *
  752. * @param
  753. * $projectId
  754. * @param
  755. * $period
  756. * @param
  757. * $anonim
  758. * @return array|int
  759. */
  760. public function frontend_widget_stats( $projectId, $from, $anonim ) {
  761. $content = '';
  762. $to = 'yesterday';
  763. $metrics = 'ga:sessions';
  764. $dimensions = 'ga:date,ga:dayOfWeekName';
  765. $options = array( 'dimensions' => $dimensions, 'quotaUser' => $this->managequota . 'p' . $projectId );
  766. $serial = 'qr2_' . $this->get_serial( $projectId . $from . $metrics );
  767. $data = $this->handle_corereports( $projectId, $from, $to, $metrics, $options, $serial );
  768. if ( is_numeric( $data ) ) {
  769. return $data;
  770. }
  771. $gadwp_data = array( array( __( "Date", 'google-analytics-dashboard-for-wp' ), __( "Sessions", 'google-analytics-dashboard-for-wp' ) ) );
  772. if ( $anonim ) {
  773. $max_array = array();
  774. foreach ( $data->getRows() as $item ) {
  775. $max_array[] = $item[2];
  776. }
  777. $max = max( $max_array ) ? max( $max_array ) : 1;
  778. }
  779. foreach ( $data->getRows() as $row ) {
  780. $gadwp_data[] = array( date_i18n( __( 'l, F j, Y', 'google-analytics-dashboard-for-wp' ), strtotime( $row[0] ) ), ( $anonim ? round( $row[2] * 100 / $max, 2 ) : (int) $row[2] ) );
  781. }
  782. $totals = $data->getTotalsForAllResults();
  783. return array( $gadwp_data, $anonim ? 0 : number_format_i18n( $totals['ga:sessions'] ) );
  784. }
  785. /**
  786. * Analytics data for Realtime component (the real-time report)
  787. *
  788. * @param
  789. * $projectId
  790. * @return array|int
  791. */
  792. private function get_realtime( $projectId ) {
  793. $metrics = 'rt:activeUsers';
  794. $dimensions = 'rt:pagePath,rt:source,rt:keyword,rt:trafficType,rt:visitorType,rt:pageTitle';
  795. try {
  796. $serial = 'qr_realtimecache_' . $this->get_serial( $projectId );
  797. $transient = GADWP_Tools::get_cache( $serial );
  798. if ( false === $transient ) {
  799. if ( $this->gapi_errors_handler() ) {
  800. return - 23;
  801. }
  802. $data = $this->service->data_realtime->get( 'ga:' . $projectId, $metrics, array( 'dimensions' => $dimensions, 'quotaUser' => $this->managequota . 'p' . $projectId ) );
  803. GADWP_Tools::set_cache( $serial, $data, 55 );
  804. } else {
  805. $data = $transient;
  806. }
  807. } catch ( Deconf_Service_Exception $e ) {
  808. $timeout = $this->get_timeouts( 'midnight' );
  809. GADWP_Tools::set_error( $e, $timeout );
  810. return $e->getCode();
  811. } catch ( Exception $e ) {
  812. $timeout = $this->get_timeouts( 'midnight' );
  813. GADWP_Tools::set_error( $e, $timeout );
  814. return $e->getCode();
  815. }
  816. if ( $data->getRows() < 1 ) {
  817. return - 21;
  818. }
  819. $i = 0;
  820. $gadwp_data = $data;
  821. foreach ( $data->getRows() as $row ) {
  822. $strip = array_map( 'wp_kses_data', $row );
  823. $gadwp_data->rows[$i] = array_map( 'esc_html', $strip );
  824. $i++;
  825. }
  826. $this->gadwp->config->options['api_backoff'] = 0;
  827. $this->gadwp->config->set_plugin_options();
  828. return array( $gadwp_data );
  829. }
  830. private function map( $map ) {
  831. $map = explode( '.', $map );
  832. if ( isset( $map[1] ) ) {
  833. $map[0] += ord( 'map' );
  834. return implode( '.', $map );
  835. } else {
  836. return str_ireplace( 'map', chr( 112 ), $map[0] );
  837. }
  838. }
  839. /**
  840. * Handles ajax requests and calls the needed methods
  841. * @param
  842. * $projectId
  843. * @param
  844. * $query
  845. * @param
  846. * $from
  847. * @param
  848. * $to
  849. * @param
  850. * $filter
  851. * @return number|Deconf_Service_Analytics_GaData
  852. */
  853. public function get( $projectId, $query, $from = false, $to = false, $filter = '', $metric = 'sessions' ) {
  854. if ( empty( $projectId ) || ! is_numeric( $projectId ) ) {
  855. wp_die( - 26 );
  856. }
  857. if ( in_array( $query, array( 'sessions', 'users', 'organicSearches', 'visitBounceRate', 'pageviews', 'uniquePageviews' ) ) ) {
  858. return $this->get_areachart_data( $projectId, $from, $to, $query, $filter );
  859. }
  860. if ( 'bottomstats' == $query ) {
  861. return $this->get_bottomstats( $projectId, $from, $to, $filter );
  862. }
  863. if ( 'locations' == $query ) {
  864. return $this->get_locations( $projectId, $from, $to, $filter, $metric );
  865. }
  866. if ( 'referrers' == $query ) {
  867. return $this->get_referrers( $projectId, $from, $to, $filter, $metric );
  868. }
  869. if ( 'contentpages' == $query ) {
  870. return $this->get_contentpages( $projectId, $from, $to, $filter, $metric );
  871. }
  872. if ( '404errors' == $query ) {
  873. $filter = $this->gadwp->config->options['pagetitle_404'];
  874. return $this->get_404errors( $projectId, $from, $to, $filter, $metric );
  875. }
  876. if ( 'searches' == $query ) {
  877. return $this->get_searches( $projectId, $from, $to, $filter, $metric );
  878. }
  879. if ( 'realtime' == $query ) {
  880. return $this->get_realtime( $projectId );
  881. }
  882. if ( 'channelGrouping' == $query || 'deviceCategory' == $query ) {
  883. return $this->get_orgchart_data( $projectId, $from, $to, $query, $filter, $metric );
  884. }
  885. if ( in_array( $query, array( 'medium', 'visitorType', 'socialNetwork', 'source', 'browser', 'operatingSystem', 'screenResolution', 'mobileDeviceBranding' ) ) ) {
  886. return $this->get_piechart_data( $projectId, $from, $to, $query, $filter, $metric );
  887. }
  888. wp_die( - 27 );
  889. }
  890. }
  891. }