tra('Google Analytics Chart'), 'description' => tra('Draw charts from Google Analytics data.'), 'prefs' => ['wikiplugin_googlechart'], 'packages_required' => ['google/apiclient' => 'Google_Client'], 'iconname' => 'bookmark', 'format' => 'html', 'introduced' => 15, 'validate' => 'all', 'params' => [ 'credentials' => [ 'name' => tra('JSON File'), 'description' => tra('Location of the (service account) credentials JSON file. Must be kept private and not web accessible. (Only needed on the first plugin on each page)'), 'required' => false, 'since' => '15.0', 'filter' => 'text', ], 'query_ids' => [ 'name' => tra('Profle IDs'), 'description' => tra('In the format ga:XXXXXXX where XXXXXXX the URL of the analytics admin.'), // see https://developers.google.com/analytics/devguides/reporting/core/v3/#user_reports or https://ga-dev-tools.appspot.com/query-explorer/ for more 'since' => '15.0', 'required' => true, 'filter' => 'text', ], 'query' => [ 'name' => tra('Query String'), 'description' => tr('Can be used instead of the parameters below. E.g. "metrics=ga:users&dimensions=ga:country", query can be generated here: %0', 'ga-dev-tools'), 'since' => '15.0', 'size' => 60, 'required' => false, 'default' => '', 'filter' => 'text', ], 'query_metrics' => [ 'name' => tra('Metrics'), 'description' => tra('e.g. "ga:pageviews", default "ga:sessions,ga:users"'), 'since' => '15.0', 'required' => false, 'default' => 'ga:sessions,ga:users', 'filter' => 'text', ], 'query_start-date' => [ 'name' => tra('Start Date'), 'description' => tra('default "30daysAgo"'), 'since' => '15.0', 'required' => false, 'default' => '30daysAgo', 'filter' => 'text', ], 'query_end-date' => [ 'name' => tra('End Date'), 'description' => tra('default "yesterday"'), 'since' => '15.0', 'required' => false, 'default' => 'yesterday', 'filter' => 'text', ], 'query_dimensions' => [ 'name' => tra('Dimensions'), 'description' => tra('default "ga:date"'), 'since' => '15.0', 'required' => false, 'default' => 'ga:date', 'filter' => 'text', ], 'query_segment' => [ 'name' => tra('Segment'), 'description' => tra('default ""'), 'since' => '15.0', 'required' => false, 'default' => '', 'filter' => 'text', ], 'query_max-results' => [ 'name' => tra('Max Results'), 'description' => tra('default 50'), 'since' => '15.0', 'required' => false, 'default' => 50, 'filter' => 'int', ], 'query_sort' => [ 'name' => tra('Sort'), 'description' => tra('One of the metrics usually, e.g. "-ga:sessions" default ""'), 'since' => '15.0', 'required' => false, 'default' => '', 'filter' => 'text', ], 'chart_container' => [ 'name' => tra('Container'), 'description' => tra('ID of a DIV to contain the chart (optional)'), 'since' => '15.0', 'required' => false, 'filter' => 'text', ], 'chart_type' => [ 'name' => tra('Chart Type'), 'description' => tra('Type of chart, e.g. LINE, PIE etc.'), 'since' => '15.0', 'required' => false, 'filter' => 'word', 'default' => 'LINE', 'options' => [ ['text' => '', 'value' => ''], ['text' => tra('Line'), 'value' => 'LINE'], ['text' => tra('Column'), 'value' => 'COLUMN'], ['text' => tra('Bar'), 'value' => 'BAR'], ['text' => tra('Pie'), 'value' => 'PIE'], ['text' => tra('Table'), 'value' => 'TABLE'], ['text' => tra('Geo'), 'value' => 'GEO'], ], ], 'width' => [ 'required' => false, 'safe' => true, 'name' => tra('Chart Width'), 'description' => tr('In pixels or percentage. Default value is %0.', '100%'), 'since' => '15.0', 'default' => '100%', 'filter' => 'text', ], 'height' => [ 'required' => false, 'safe' => true, 'name' => tra('Chart Height'), 'description' => tr('In pixels or percentage. Default value is %0.', '300'), 'since' => '15.0', 'default' => '300', 'filter' => 'text', ], 'float' => [ 'required' => false, 'name' => tra('Float Position'), 'description' => tr('Set the alignment for the entire element. For elements with a width of less than 100%, other elements will wrap around it unless the %0 parameter is appropriately set.', 'clear'), 'since' => '15.0', 'filter' => 'alpha', 'safe' => true, 'default' => '', 'options' => [ ['text' => '', 'value' => ''], ['text' => tra('Right'), 'value' => 'right'], ['text' => tra('Left'), 'value' => 'left'], ['text' => tra('None'), 'value' => 'none'], ], ], ], ]; } function wikiplugin_googlechart($data, $params) { if (! class_exists('Google_Client')) { Feedback::error(tr('To draw charts from Google Analytics, Tiki needs the google/apiclient package. If you do not have permission to install this package, ask the site administrator.')); return; } static $id = 0; $unique = 'wpdialog_' . ++$id; $headerlib = TikiLib::lib('header'); $defaults = []; $plugininfo = wikiplugin_googlechart_info(); foreach ($plugininfo['params'] as $key => $param) { $defaults["$key"] = $param['default']; } $params = array_merge($defaults, $params); if ($id === 1 && (empty($params['credentials']) || ! is_readable($params['credentials']))) { return tra('googlechart: No credentials file.'); } if (empty($params['query_ids'])) { return tra('googlechart: No query_ids supplied.'); } if (empty($params['chart_container'])) { $params['chart_container'] = $unique; } if (is_numeric($params['width'])) { $params['width'] .= 'px'; } if (is_numeric($params['height'])) { $params['height'] .= 'px'; } if (! empty($params['float'])) { $params['float'] = ' float:' . $params['float']; } if (! empty($params['class'])) { $params['class'] = ' class="' . $params['class'] . '"'; } else { $params['class'] = ' class="wp_googlechart"'; } // to business, the magic word... $access_token = wikiplugin_googlechart_authenticate($params['credentials']); $js = '(function (w, d, s, g, js, fs) { g = w.gapi || (w.gapi = {}); g.analytics = { q: [], ready: function (f) { this.q.push(f); } }; js = d.createElement(s); fs = d.getElementsByTagName(s)[0]; js.src = "https://apis.google.com/js/platform.js"; fs.parentNode.insertBefore(js, fs); js.onload = function () { g.load("analytics"); }; }(window, document, "script"));'; $headerlib->add_js($js); $query = []; if (! empty($params['query'])) { parse_str($params['query'], $query); if (empty($query['ids'])) { $query['ids'] = $params['query_ids']; } } $queryp = []; foreach ($params as $key => $param) { if (strpos($key, 'query_') === 0) { $queryp[substr($key, strlen('query_'))] = $param; } } $query = array_merge($queryp, $query); $query = json_encode(array_filter($query)); $chart = ['options' => ['width' => '100%']]; foreach ($params as $key => $param) { if (strpos($key, 'chart_') === 0) { $chart[substr($key, strlen('query_'))] = $param; } } $chart = json_encode(array_filter($chart)); $js = " gapi.analytics.ready(function () { try { /** * Authorize the user with an access token obtained server side. */ gapi.analytics.auth.authorize({ 'serverAuth': { 'access_token': '$access_token' } }); } catch (e) { console.log('Chart error: ' + e); } }); "; $headerlib->add_js($js, 15); // auth can be shared $js = " gapi.analytics.ready(function () { try { var dataChart1 = new gapi.analytics.googleCharts.DataChart({ query: {$query}, chart: {$chart} }); dataChart1.execute(); } catch (e) { console.log('Chart \"$unique\" error: ' + e.message); } }); "; $headerlib->add_js($js, 16); $return = '
'; return $return; } function wikiplugin_googlechart_authenticate($credentials_file) { $client = new Google_Client(); $token = isset($_SESSION['ga_access_token']) && $_SESSION['ga_access_token'] ? $_SESSION['ga_access_token'] : false; if (empty($token) || $token['created'] + 3600 < time()) { // in v2 it will be $token['expires_in'] but hard coded to 3600 for v1 api $data = json_decode(file_get_contents($credentials_file)); $cred = new Google_Auth_AssertionCredentials( $data->client_email, [Google_Service_Analytics::ANALYTICS_READONLY], $data->private_key ); $client->setAssertionCredentials($cred); if ($client->getAuth()->isAccessTokenExpired()) { $client->getAuth()->refreshTokenWithAssertion($cred); } $client->setApplicationName("The Networked Planet Test App"); $client->setAccessType('offline'); $client->addScope(Google_Service_Analytics::ANALYTICS_READONLY); $token = $client->getAccessToken(); $token = json_decode($token, true); $_SESSION['ga_access_token'] = $token; } if ($token) { try { $client->setAccessToken(json_encode($_SESSION['ga_access_token'])); } catch (Exception $e) { Feedback::error(tr('googlechart exception: %0', $e->getMessage())); return false; } return $token['access_token']; } else { // no token return false; } }