You are here

optimizely.module in Optimizely 8.0

Optimizely module

Originally for Visual Website Optimizer by Awesome Software, http://www.awesome-software.net/ and Ted Cooper (ELC) http://drupal.org/user/784944

Ported to Optimizely by netstudio.gr and Yannis Karampelas (http://drupal.org/user/1145950).

Adds Optimizely javascript (snippet) to the page which loads the A/B test from the optimizely.com website.

7.x-2.x Functionality added to support multiple project entries from an Optimizely account. Each entry specifies target paths to include the Optimizely projects on the specific site paths. Targeting paths eliminates the need to load Optimizely tests on every page of a site.

7.x-2.x by Darren "Dee" Lee (DeeZone: http://drupal.org/user/288060) and Peter Lehrer (plehrer: http://drupal.org/user/2257350) Sponsored by DoSomething.org (http://www.dosomething.org)

8.x-dev by Earl Fong (tz_earl: https://www.drupal.org/user/2531114/)

File

optimizely.module
View source
<?php

/**
 * @file
 * Optimizely module
 *
 * Originally for Visual Website Optimizer by Awesome Software, http://www.awesome-software.net/
 * and Ted Cooper (ELC) http://drupal.org/user/784944
 *
 * Ported to Optimizely by netstudio.gr and Yannis Karampelas (http://drupal.org/user/1145950).
 *
 * Adds Optimizely javascript (snippet) to the page which loads the A/B test from
 * the optimizely.com website.
 *
 * 7.x-2.x Functionality added to support multiple project entries from an Optimizely account. Each entry
 * specifies target paths to include the Optimizely projects on the specific site paths. Targeting paths
 * eliminates the need to load Optimizely tests on every page of a site.
 *
 * 7.x-2.x by Darren "Dee" Lee (DeeZone: http://drupal.org/user/288060)
 * and Peter Lehrer (plehrer: http://drupal.org/user/2257350)
 * Sponsored by DoSomething.org (http://www.dosomething.org)
 *
 * 8.x-dev by Earl Fong (tz_earl: https://www.drupal.org/user/2531114/)
 */

/**
 * Implements hook_help().
 *
 * Help text related to the module's functionality and use.
 */
function optimizely_help($route_name, $arg) {
  switch ($route_name) {
    case 'help.page.optimizely':
      return t('<p><a href="http://optimize.ly/OZRdc0">Optimizely</a> is a third' . ' party service to add A/B testing to a web site. The tests are applied to' . ' the site by loading javascript snippets generated by the Optimizely web' . ' site. The generated javascript files are applied to certain paths on the' . ' site based on Project entries managed by the Optimizely module. To start' . ' to apply the general, sitewide Optimizely javascript file the' . '
         <a href="@settings">Optimizely account ID</a> must be entered in the' . ' module administration page.</p>' . '<p>Enable or disable each project entry to apply the project settings' . ' while not needing to remove the actual entry. The default entry can be' . ' disabled when additional project entries are made with more specific' . '
         settings. This can include using the orginal Project Code.</p>', array(
        '@settings' => \Drupal::url('optimizely.settings'),
      ));
    case 'optimizely.listing':
      return t('<p>A listing of the Optimizely projects. Each entry can be' . ' enabled / disabled for specific or wildcard paths. Enabled entries are' . ' highlighted in green while disabled entries are in red. The top,' . ' "Default" entry cannot be deleted but its settings can be adjusted or' . ' completely disabled.</p>' . '<p>The goal of having multiple projects is to minimize the size of the' . ' Optimizely hosted javascript file. If all experiments are contained in a' . ' single file and processed on every page load there may be an issue with' . ' increased page load time. Having multiple projects and loading them on' . ' specific paths that apply to the experiments helps to minimize the size' . " of the file and eliminate processing unused javascript on the user's" . ' browser.</p>');
    case 'optimizely.add_update':
    case 'optimizely.add_update.oid':
      return t('Add or edit specific project entries. Each entry should have an' . ' Optimizely project / experiment assigned to it, as well as a range of' . ' website paths where the Optimizely javascript hosted file should be' . ' included.');
    case 'optimizely.settings':
      return t('Add the Optimizely account ID supplied by the Optimizely website.' . ' The account ID is essential to setting up the initial sitewide default' . ' project entry.');
  }
}

/**
 * Implements hook_theme().
 *
 * All of the template definitions. All related templates can be found in
 * /template in the module folder. The template layout can be overridden
 * by the site theme by copying these template files into theme directory.
 */
function optimizely_theme($existing, $type, $theme, $path) {
  return array(
    'optimizely_account_settings_form' => array(
      'render element' => 'form',
      'template' => 'optimizely-account-settings-form',
    ),
    'optimizely_add_update_form' => array(
      'render element' => 'form',
      'template' => 'optimizely-add-update-form',
    ),
  );
}

/**
 * Implements hook_page_build().
 *
 * Checks each page that is about to be rendered as to
 * whether to insert a snippet of Optimizely code or not.
 */
function optimizely_page_attachments(array &$page) {
  $url = \Drupal\Core\Url::fromRoute('<current>');
  $current_path = $url
    ->toString();
  $current_path_alias = _lookup_path_alias($current_path);
  $add_snippet = FALSE;

  // Load all entries in the optimizely table
  $query = db_select('optimizely', 'o', array(
    'target' => 'slave',
  ))
    ->fields('o')
    ->condition('o.enabled', 1, '=')
    ->orderBy('oid');

  // Fetch the result set.
  $result = $query
    ->execute();
  if (!$result) {
    return;
  }

  // Query results found
  // Loop through each row of the found results
  while ($project = $result
    ->fetchAssoc()) {

    // Only process the entries that are enabled
    if (!$project['enabled']) {
      continue;
    }

    // Target paths from database for project and get real path for <front> if a part of target paths for project
    $project_paths = unserialize($project['path']);
    $front_index = array_search('<front>', $project_paths);
    if (is_int($front_index)) {
      $project_paths[$front_index] = \Drupal::config('system.site')
        ->get('page.front');
    }

    // Check all paths for alias or sytem URL values, foreach loop based on
    // orginal array value, addiitonal values added will not effect the array
    foreach ($project_paths as $proj_path) {

      // Remove parameters
      if (strpos($proj_path, '?') !== FALSE) {
        $proj_path = substr($proj_path, 0, strpos($proj_path, '?'));
        if (stristr($current_path, $proj_path) || stristr(_lookup_path_alias($current_path), $proj_path) || stristr(_lookup_system_path($current_path), $proj_path)) {
          $add_snippet = TRUE;
          break 2;
        }
      }

      // Look for wildcard match
      if (strpos($proj_path, '*') !== FALSE) {

        // Sitewide wild card
        if ($proj_path == '*') {
          $add_snippet = TRUE;
          break 2;
        }
        else {

          // Remove wildcard, get base path(s)
          $proj_path = substr($proj_path, 0, -2);

          // Look for wildcard match
          if (stristr($current_path, $proj_path) || stristr(_lookup_path_alias($current_path), $proj_path) || stristr(_lookup_system_path($current_path), $proj_path)) {
            $add_snippet = TRUE;
            break 2;
          }
        }
      }

      // Build out $project_paths with possible source and alias values to
      // check for path matching.
      $proj_path_source = _lookup_system_path($proj_path);
      if ($proj_path_source) {
        $project_paths[] = $proj_path_source;
      }
      $proj_path_alias = _lookup_path_alias($proj_path);
      if ($proj_path_alias) {
        $project_paths[] = $proj_path_alias;
      }
    }

    // Prep for path matching.
    $project_paths = implode("\n", array_unique($project_paths));
    if (\Drupal::service('path.matcher')
      ->matchPath($current_path, $project_paths) || \Drupal::service('path.matcher')
      ->matchPath($current_path_alias, $project_paths)) {
      $add_snippet = TRUE;
      break;
    }
  }
  if ($add_snippet) {

    // Add javascript call to page markup
    $snippet_url = '//cdn.optimizely.com/js/' . $project['project_code'] . '.js';
    $page['#attached']['html_head'][] = array(
      array(
        '#tag' => 'script',
        '#attributes' => array(
          'type' => 'text/javascript',
          'src' => $snippet_url,
        ),
        '#value' => '',
      ),
      'optimizely-snippet',
    );
  }
}

/**
 * These three functions are redundant with trait LookupPath,
 * but I haven't been able to re-use the trait in this .module file.
 */
function _lookup_path_alias($path) {
  $path = _check_path($path);
  $alias = \Drupal::service('path.alias_manager')
    ->getAliasByPath($path);
  return strcmp($alias, $path) == 0 ? FALSE : $alias;
}
function _lookup_system_path($alias) {
  $alias = _check_path($alias);
  $path = \Drupal::service('path.alias_manager')
    ->getPathByAlias($alias);
  return strcmp($path, $alias) == 0 ? FALSE : $path;
}
function _check_path($path) {
  return $path[0] == '/' ? $path : '/' . $path;
}

Functions

Namesort descending Description
optimizely_help Implements hook_help().
optimizely_page_attachments Implements hook_page_build().
optimizely_theme Implements hook_theme().
_check_path
_lookup_path_alias These three functions are redundant with trait LookupPath, but I haven't been able to re-use the trait in this .module file.
_lookup_system_path