ga_reports-8.x-1.0/ga_reports.module

ga_reports.module
<?php

/**
 * @file
 * Drupal Module: Google Analytics Reports Module.
 */

use Drupal\Component\Serialization\Json;
use Drupal\ga_reports\GaReportsApiFeed;
use Drupal\Core\Cache\CacheableRedirectResponse;
use Drupal\Core\Url;
use Drupal\ga_reports\Component\Render\GoogleAnalyticsJavaScriptSnippet;

/**
 * @file
 * Front-end interfaces that use the Google Analytics Reports API module.
 */

/**
 * Implements hook_ga_reports_field_import_alter().
 */
function ga_reports_field_import_alter(&$field) {
  // Change data type for Date field.
  if ($field['id'] == 'date') {
    $field['attributes']['dataType'] = 'date';
  }
}

/**
 * Implements hook_ga_reports_reported_data_alter().
 */
function ga_reports_reported_data_alter(&$name, &$value) {
  // Get all Google Analytics fields.
  $fields = ga_reports_get_fields();

  // Date and time datatypes should not have the digits after the zero.
  if ((isset($fields[$name])) && (in_array($fields[$name]->data_type, ['date', 'time']))) {
    $value = round($value);
  }

  switch ($name) {
    case 'userType':
      $value = ($value == 'New Visitor') ? t('New Visitor') : t('Returning Visitor');
      break;

    case 'date':
      $value = strtotime($value);
      break;

    case 'yearMonth':
      $value = strtotime($value . '01');
      break;

    case 'userGender':
      $value = ($value == 'male') ? t('Male') : t('Female');
      break;
  }
}

/**
 * List of Google Analytics dimensions and metrics.
 *
 * @return array
 *   An associative array containing list of Google Analytics column objects.
 *   Each object is associative array containing:
 *   - gid: The primary identifier for a column.
 *   - type: The type of column.
 *   - data_type: The type of data this column represents.
 *   - column_group: The dimensions/metrics group the column belongs to.
 *   - ui_name: The name/label of the column used in user interfaces (UI).
 *   - description: The full description of the column.
 *   - calculation: This shows how the metric is calculated.
 */
function ga_reports_get_fields() {
  $fields = &drupal_static(__FUNCTION__);
  // todo: fetch data from cache.
  if (!isset($fields)) {
    $fields = \Drupal::database()->select('ga_reports_fields', 'g')
      ->fields('g')
      ->execute()
      ->fetchAllAssoc('gaid');
  }
  return $fields;
}

/**
 * Determines if a field is custom or not.
 */
function ga_reports_is_custom($field) {
  return preg_match('/XX/', $field) ? TRUE : FALSE;
}

/**
 * Converts a base custom field name and number into a specific field name.
 */
function ga_reports_custom_to_variable_field($field, $number) {
  return preg_replace('/XX/', $number, $field);
}

/**
 * Converts a specific field name into a base custom field name.
 */
function ga_reports_variable_to_custom_field($field) {
  return preg_replace('/\d+/', 'XX', $field);
}

/**
 * Instantiate a new GaReportsApiFeed object.
 *
 * @return object
 *   GaReportsApiFeed object to authorize access and request data
 *   from the Google Analytics Core Reporting API.
 */
function ga_reports_gafeed() {
  $config = \Drupal::configFactory()->getEditable('ga_reports_api.settings');

  // If the access token is still valid, return an authenticated
  // GaReportsApiFeed.
  $access_token = $config->get('access_token');

  if ($access_token && time() < $config->get('expires_at')) {
    return new GaReportsApiFeed($access_token);
  }
  else {
    // If the site has an access token and refresh token, but the access
    // token has expired, authenticate the user with the refresh token.
    $refresh_token = $config->get('refresh_token');
    if ($refresh_token) {
      try {
        $ga_reports_feed = new GaReportsApiFeed();
        $ga_reports_feed->refreshToken($config->get('client_id'), $config->get('client_secret'), $refresh_token);

        $config
          ->set('access_token', $ga_reports_feed->accessToken)
          ->set('expires_at', $ga_reports_feed->expiresAt)
          ->save();

        return $ga_reports_feed;
      }
      catch (\Exception $e) {
        drupal_set_message(t('There was an authentication error. Message: @message.', ['@message' => $e->getMessage()]), 'error', FALSE);
        \Drupal::logger('ga_reports_api')->error('There was an authentication error. Message: @message.', ['@message' => $e->getMessage()]);
        return NULL;
      }
    }
    else {
      // If there is no access token or refresh token and client is returned
      // to the config page with an access code, complete the authentication.
      if (isset($_GET['code'])) {
        try {
          $ga_reports_feed = new GaReportsApiFeed();
          $redirect_uri = $config->get('redirect_uri');

          $ga_reports_feed->finishAuthentication($config->get('client_id'), $config->get('client_secret'), $redirect_uri);

          $config
            ->set('access_token', $ga_reports_feed->accessToken)
            ->set('expires_at', $ga_reports_feed->expiresAt)
            ->set('refresh_token', $ga_reports_feed->refreshToken)
            ->clear('redirect_uri')
            ->save();

          drupal_set_message(t('You have been successfully authenticated.'));

          $response = new CacheableRedirectResponse(Url::fromUri($redirect_uri)->toString());
          $response->send();
        }
        catch (Exception $e) {
          drupal_set_message(t('There was an authentication error. Message: @message.', ['@message' => $e->getMessage()]), 'error', FALSE);
          \Drupal::logger('ga_reports_api')->error('There was an authentication error. Message: @message.', ['@message' => $e->getMessage()]);
          return NULL;
        }
      }
    }
  }
}

/**
 * Request report data.
 *
 * @array $params
 *   An associative array containing:
 *   - profile_id: required
 *    [default=variable_get('ga_reports_profile_id')].
 *   - metrics: required.
 *   - dimensions: optional [default=none].
 *   - sort_metric: optional [default=none].
 *   - filters: optional [default=none].
 *   - segment: optional [default=none].
 *   - start_date: optional [default=2005-01-01].
 *   - end_date: optional [default=today].
 *   - start_index: optional [default=1].
 *   - max_results: optional [default=10,000].
 * @array $cache_options
 *   An optional associative array containing:
 *   - cid: optional [default=md5 hash].
 *   - expire: optional [default=CACHE_TEMPORARY].
 *   - refresh: optional [default=FALSE].
 *
 * @return object
 *   GaReportsApiFeed object to authorize access and request data
 *   from the Google Analytics Core Reporting API after reporting data.
 */
function ga_reports_report_data($params = [], $cache_options = []) {
  $config = \Drupal::config('ga_reports_api.settings');
  if (isset($params['profile_id'])) {
    $params['profile_id'] = 'ga:' . $params['profile_id'];
  }
  else {
    $params['profile_id'] = 'ga:' . $config->get('profile_id');
  }

  $ga_feed = ga_reports_gafeed();

  if ($ga_feed) {
    $ga_feed->queryReportFeed($params, $cache_options);
    return $ga_feed;
  }
  else {
    drupal_set_message(t('There was an authentication error. Please check your Google Analytics API settings and try again.'), 'error', FALSE);
    \Drupal::logger('ga_reports_api')->error('There was an authentication error. Please check your Google Analytics API settings and try again.');
    return ['error' => TRUE];
  }
}

/**
 * Programmatically revoke token.
 */
function ga_reports_revoke() {
  $ga_feed = ga_reports_gafeed();
  $ga_feed->revokeToken();

  $config = \Drupal::configFactory()->getEditable('ga_reports_api.settings');
  // Delete module variables.
  $config
    ->clear('access_token')
    ->clear('client_id')
    ->clear('client_secret')
    ->clear('default_page')
    ->clear('expires_at')
    ->clear('profile_id')
    ->clear('redirect_uri')
    ->clear('refresh_token')
    ->save();

}

/**
 * Sets the expiry timestamp for cached queries.
 *
 * Default is 3 days.
 *
 * @return int
 *   The UNIX timestamp to expire the query at.
 */
function ga_reports_cache_time() {
  return time() + \Drupal::config('ga_reports_api.settings')->get('cache_length');
}

/**
 * Google Analytics reports profiles for current authorized user.
 *
 * @return arraynull
 *   An associative array containing:
 *   - options: list of current available profiles.
 *   - profile_id: current default profile id.
 *   - current_profile: current default profile object.
 */
function ga_reports_profiles_list() {
  $config = \Drupal::configFactory()->getEditable('ga_reports_api.settings');
  $account = ga_reports_gafeed();
  if (($account) && ($account->isAuthenticated())) {
    $web_properties = NULL;
    $web_properties_obj = $account->queryWebProperties();

    if (isset($web_properties_obj->results->items)) {
      $web_properties = $web_properties_obj->results->items;
    }

    $profiles_obj = $account->queryProfiles();
    $profiles = [];
    if (isset($profiles_obj->results->items)) {
      $profiles = $profiles_obj->results->items;
    }

    $options = [];
    $profile_id = $config->get('profile_id');

    $config_ga = \Drupal::config('ga.settings');
    $ga_account = $config_ga->get('account') ? $config_ga->get('account') : NULL;

    $set_default = FALSE;

    // Add optgroups for each web property.
    if (!empty($profiles)) {
      foreach ($profiles as $profile) {
        $web_property = NULL;
        foreach ($web_properties as $web_property_value) {
          if ($web_property_value->id == $profile->webPropertyId) {
            $web_property = $web_property_value;
            break;
          }
        }

        $options[$web_property->name][$profile->id] = $profile->name . ' (' . $profile->id . ')';
        // Find current site in the account list.
        if (empty($profile_id)) {
          // If Google Analytics module is enabled check it first.
          if (isset($ga_account) && ($ga_account == $profile->webPropertyId)) {
            $profile_id = $profile->id;
            $set_default = TRUE;
          }
          // Rough attempt to see if the current site is in the account list.
          elseif (parse_url($web_property->websiteUrl, PHP_URL_HOST) == $_SERVER['HTTP_HOST']) {
            $profile_id = $profile->id;
            $set_default = TRUE;
          }
        }
      }
    }

    // If no profile ID is set yet, set the first profile in the list.
    if (empty($profile_id)) {
      if (count($options)) {
        $profile_id = key($options[key($options)]);
        $set_default = TRUE;
      }
    }

    if ($set_default) {
      $config
        ->set('profile_id', $profile_id)
        ->save();
    }

    $current_profile = NULL;

    // Load current profile object.
    foreach ($profiles as $profile) {
      if ($profile->id == $profile_id) {
        $current_profile = $profile;
        $config
          ->set('default_page', isset($current_profile->defaultPage) ? '/' . $current_profile->defaultPage : '/')
          ->save();
        break;
      }
    }
    $return = [
      'options' => $options,
      'profile_id' => $profile_id,
      'current_profile' => $current_profile,
    ];

    return $return;
  }
}

/**
 * Implements hook_page_attachments().
 *
 * Insert JavaScript to the appropriate scope/region of the page.
 */
function ga_reports_page_attachments(array &$page) {
  // Get page http status code for visibility filtering.
  $config = \Drupal::config('ga_reports_api.settings');
  $id = $config->get('account');
  // 1. Check if the GA account number has a valid value.
  // 2. Track page views based on visibility value.
  // 3. Check if we should track the currently active user's role.
  // 4. Ignore pages visibility filter for 404 or 403 status codes.
  if (preg_match('/^UA-\d+-\d+$/', $id)) {
    // Add messages tracking.
    $message_events = '';
    if ($message_types = $config->get('track.messages')) {
      $message_types = array_values(array_filter($message_types));
      $status_heading = [
        'status' => t('Status message'),
        'warning' => t('Warning message'),
        'error' => t('Error message'),
      ];

      foreach (drupal_get_messages(NULL, FALSE) as $type => $messages) {
        // Track only the selected message types.
        if (in_array($type, $message_types)) {
          foreach ($messages as $message) {
            // @todo: Track as exceptions?
            $message_events .= 'ga("send", "event", ' . Json::encode(t('Messages')) . ', ' . Json::encode($status_heading[$type]) . ', ' . Json::encode(strip_tags($message)) . ');';
          }
        }
      }
    }

    // Build tracker code.
    $script = '(function(i,s,o,g,r,a,m){';
    $script .= 'i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){';
    $script .= '(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),';
    $script .= 'm=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)';
    $script .= '})(window,document,"script",';
    $debug = '';
    // Which version of the tracking library should be used?
    $library_tracker_url = 'https://www.google-analytics.com/' . ($debug ? 'analytics_debug.js' : 'analytics.js');

    // Should a local cached copy of analytics.js be used?
    if ($config->get('cache') && $url = _ga_reports_cache($library_tracker_url)) {
      // A dummy query-string is added to filenames, to gain control over
      // browser-caching. The string changes on every update or full cache
      // flush, forcing browsers to load a new copy of the files, as the
      // URL changed.
      $query_string = '?' . (\Drupal::state()->get('system.css_js_query_string') ?: '0');

      $script .= '"' . $url . $query_string . '"';
    }
    else {
      $script .= '"' . $library_tracker_url . '"';
    }
    $script .= ',"ga");';
    // Build the create only fields list.
    $create_only_fields = ['cookieDomain' => 'auto'];

    // Domain tracking type.
    global $cookie_domain;
    $domain_mode = $config->get('domain_mode');
    $googleanalytics_adsense_script = '';

    // Per RFC 2109, cookie domains must contain at least one dot other than the
    // first. For hosts such as 'localhost' or IP Addresses we don't set a
    // cookie domain.
    if ($domain_mode == 1 && count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
      $create_only_fields = array_merge($create_only_fields, ['cookieDomain' => $cookie_domain]);
      $googleanalytics_adsense_script .= 'window.google_analytics_domain_name = ' . Json::encode($cookie_domain) . ';';
    }
    elseif ($domain_mode == 2) {
      // Cross Domain tracking. 'autoLinker' need to be enabled in 'create'.
      $create_only_fields = array_merge($create_only_fields, ['allowLinker' => TRUE]);
      $googleanalytics_adsense_script .= 'window.google_analytics_domain_name = "none";';
    }
    // Create a tracker.
    $script .= 'ga("create", ' . Json::encode($id) . ', ' . Json::encode($create_only_fields) . ');';

    // Prepare Adsense tracking.
    $googleanalytics_adsense_script .= 'window.google_analytics_uacct = ' . Json::encode($id) . ';';
    $page['#attached']['html_head'][] = [
        [
          '#tag' => 'script',
          '#value' => new GoogleAnalyticsJavaScriptSnippet($script),
        ],
      'google_analytics_tracking_script',
    ];
  }

}

/**
 * Download/Synchronize/Cache tracking code file locally.
 *
 * @string $location
 *   The full URL to the external javascript file.
 * @bool $synchronize
 *   Synchronize to local cache if remote file has changed.
 *
 * @return mixed
 *   The path to the local javascript file on success, boolean FALSE on failure.
 */
function _ga_reports_cache($location, $synchronize = FALSE) {
  $path = 'public://google_analytics';
  $file_destination = $path . '/' . basename($location);

  if (!file_exists($file_destination) || $synchronize) {
    // Download the latest tracking code.
    try {
      $data = (string) \Drupal::httpClient()
        ->get($location)
        ->getBody();

      if (file_exists($file_destination)) {
        // Synchronize tracking code and and replace local file if outdated.
        $data_hash_local = Crypt::hashBase64(file_get_contents($file_destination));
        $data_hash_remote = Crypt::hashBase64($data);
        // Check that the files directory is writable.
        if ($data_hash_local != $data_hash_remote && file_prepare_directory($path)) {
          // Save updated tracking code file to disk.
          file_unmanaged_save_data($data, $file_destination, FILE_EXISTS_REPLACE);
          // Based on Drupal Core class AssetDumper.
          if (extension_loaded('zlib') && \Drupal::config('system.performance')->get('js.gzip')) {
            file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $file_destination . '.gz', FILE_EXISTS_REPLACE);
          }
          \Drupal::logger('google_analytics')->info('Locally cached tracking code file has been updated.');

          // Change query-strings on css/js files to enforce reload for all
          // users.
          _drupal_flush_css_js();
        }
      }
      else {
        // Check that the files directory is writable.
        if (file_prepare_directory($path, FILE_CREATE_DIRECTORY)) {
          // There is no need to flush JS here as core refreshes JS caches
          // automatically, if new files are added.
          file_unmanaged_save_data($data, $file_destination, FILE_EXISTS_REPLACE);
          // Based on Drupal Core class AssetDumper.
          if (extension_loaded('zlib') && \Drupal::config('system.performance')->get('js.gzip')) {
            file_unmanaged_save_data(gzencode($data, 9, FORCE_GZIP), $file_destination . '.gz', FILE_EXISTS_REPLACE);
          }
          \Drupal::logger('google_analytics')->info('Locally cached tracking code file has been saved.');

          // Return the local JS file path.
          return file_url_transform_relative(file_create_url($file_destination));
        }
      }
    }
    catch (RequestException $exception) {
      watchdog_exception('google_analytics', $exception);
    }
  }
  else {
    // Return the local JS file path.
    return file_url_transform_relative(file_create_url($file_destination));
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc