You are here

cdn.module in CDN 6

Same filename and directory in other branches
  1. 8.3 cdn.module
  2. 5 cdn.module
  3. 6.2 cdn.module
  4. 7.2 cdn.module

Implementation of the core hooks, defines, public and private functions.

File

cdn.module
View source
<?php

/**
 * @file
 * Implementation of the core hooks, defines, public and private functions.
 */
define('CDN_DISABLED', 0);
define('CDN_DEBUG', 1);
define('CDN_ENABLED', 2);

// Permissions.
define('CDN_PERM_ACCESS_STATS', 'access per-page statistics');
define('CDN_PERM_ACCESS_DEBUG', 'access files on CDN when in debug mode');

// Variables and values.
define('CDN_MODE_VARIABLE', 'cdn_mode');
define('CDN_MODE_BASIC', 'basic');
define('CDN_MODE_ADVANCED', 'advanced');
define('CDN_STATUS_VARIABLE', 'cdn_status');
define('CDN_STATS_VARIABLE', 'cdn_stats');
define('CDN_DRUPAL_ROOT_VARIABLE', 'cdn_drupal_root');

// Variables for basic mode.
define('CDN_BASIC_URL_VARIABLE', 'cdn_basic_url');
define('CDN_BASIC_EXTENSIONS_VARIABLE', 'cdn_basic_extensions');
define('CDN_BASIC_EXTENSIONS_DEFAULT', '.css .js .ico .gif .jpg .jpeg .png .svg .otf .ttf .swf');

// Variables for advanced mode.
define('CDN_ADVANCED_SYNCED_FILES_DB_VARIABLE', 'cdn_advanced_synced_files_db');

// Hardcoded settings for accessing the daemon's metadata, to show statistics.
define('CDN_DAEMON_SYNCED_FILES_DB', 'synced_files.db');
define('CDN_DAEMON_PERSISTENT_DATA_DB', 'persistent_data.db');
define('CDN_DAEMON_FSMONITOR_DB', 'fsmonitor.db');
define('CDN_DAEMON_PID_FILE', 'daemon.pid');

//----------------------------------------------------------------------------

// Drupal core.
function custom_file_url_rewrite($path) {
  $status = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
  $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
  $stats = variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS);
  if ($stats) {
    require_once drupal_get_path('module', 'cdn') . '/cdn.stats.inc';
  }
  if ($status == CDN_ENABLED || $status == CDN_DEBUG && user_access(CDN_PERM_ACCESS_DEBUG)) {
    if ($stats) {
      $start = microtime();
    }

    // Depending on the mode, use a different function to get the URL.
    if ($mode == CDN_MODE_BASIC) {
      $cdn_url = cdn_basic_get_url($path);
      $server = FALSE;
    }
    else {
      list($cdn_url, $server) = cdn_advanced_get_url($path);
    }

    // If the user can access it, add this to the per-page statistics.
    if ($stats) {
      $end = microtime();
      _cdn_devel_page_stats($path, $cdn_url, $server, $end - $start);
    }
    return $cdn_url;
  }
  else {
    return FALSE;
  }
}

/**
 * Implementation of hook_menu().
 */
function cdn_menu() {
  $items['admin/settings/cdn'] = array(
    'title' => 'CDN integration',
    'description' => 'Configure various settings for CDN integration.',
    'access arguments' => array(
      'administer site configuration',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_settings_form',
    ),
    'type' => MENU_NORMAL_ITEM,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/settings'] = array(
    'title' => 'Settings',
    'access arguments' => array(
      'administer site configuration',
    ),
    'weight' => -10,
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/basic'] = array(
    'title' => 'Basic mode',
    'description' => 'Basic mode settings.',
    'access arguments' => array(
      'administer site configuration',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_basic_settings_form',
    ),
    'weight' => -8,
    'type' => MENU_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/advanced'] = array(
    'title' => 'Advanced mode',
    'description' => 'Advanced mode settings.',
    'access arguments' => array(
      'administer site configuration',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_advanced_settings_form',
    ),
    'weight' => -6,
    'type' => MENU_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );
  $items['admin/settings/cdn/other'] = array(
    'title' => 'Other',
    'description' => 'Other settings.',
    'access arguments' => array(
      'administer site configuration',
    ),
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'cdn_admin_other_settings_form',
    ),
    'weight' => -4,
    'type' => MENU_LOCAL_TASK,
    'file' => 'cdn.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function cdn_perm() {
  return array(
    CDN_PERM_ACCESS_STATS,
    CDN_PERM_ACCESS_DEBUG,
  );
}

/**
 * Implementation of hook_theme().
 */
function cdn_theme() {
  return array(
    'cdn_page_stats' => array(
      'file' => 'theme.inc',
      'arguments' => array(
        $file_count => NULL,
        $cdn_file_count => NULL,
        $synced_files_per_server_count => NULL,
        $total_time => NULL,
        $synced_files => NULL,
        $unsynced_files => NULL,
      ),
    ),
  );
}

/**
 * Implementation of hook_exit().
 */
function cdn_exit($destination = NULL) {
  require_once drupal_get_path('module', 'cdn') . '/cdn.stats.inc';

  // Try not to break non-HTML pages.
  if (function_exists('drupal_get_headers')) {
    $headers = drupal_get_headers();
    $formats = array(
      'xml',
      'javascript',
      'json',
      'plain',
      'image',
      'application',
      'csv',
      'x-comma-separated-values',
    );
    foreach ($formats as $format) {
      if (strstr($headers, $format)) {
        return;
      }
    }
  }
  if (!$destination && variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED) != CDN_DISABLED && variable_get(CDN_STATS_VARIABLE, FALSE) && user_access(CDN_PERM_ACCESS_STATS)) {
    list($file_count, $cdn_file_count, $synced_files_per_server_count, $total_time, $synced_files, $unsynced_files, ) = _cdn_devel_page_stats();
    print theme('cdn_page_stats', $file_count, $cdn_file_count, $synced_files_per_server_count, $total_time, $synced_files, $unsynced_files);
  }
}

/**
 * Implementation of hook_requirements().
 */
function cdn_requirements($phase) {
  $requirements = array();
  $t = get_t();
  switch ($phase) {
    case 'install':
    case 'runtime':
      $status = variable_get(CDN_STATUS_VARIABLE, CDN_DISABLED);
      $mode = variable_get(CDN_MODE_VARIABLE, CDN_MODE_BASIC);
      $requirements['cdn']['title'] = $t('CDN integration');

      // Set the basic info: disabled/debug/enabled.
      if ($status == CDN_DISABLED) {
        $requirements['cdn'] += array(
          'description' => $t('CDN integration is disabled for all users.'),
          'severity' => REQUIREMENT_WARNING,
          'value' => $t('Disabled'),
        );
      }
      elseif ($status == CDN_DEBUG) {
        $requirements['cdn'] += array(
          'description' => $t('CDN integration is only enabled for users with
                               the %cdn-debug-mode-permission permission', array(
            '%cdn-debug-mode-permission' => CDN_PERM_ACCESS_DEBUG,
          )),
          'severity' => REQUIREMENT_WARNING,
          'value' => $t('In debug mode'),
        );
      }
      else {
        $requirements['cdn'] += array(
          'description' => $t('CDN integration is enabled for all users.'),
          'severity' => REQUIREMENT_OK,
          'value' => $t('Enabled'),
        );
      }

      // When CDN integration is enabled, add on more information.
      if ($status != CDN_DISABLED) {
        if ($mode == CDN_MODE_BASIC) {
          $requirements['cdn']['value'] .= ' – ' . t('basic mode');
        }
        else {
          $requirements['cdn']['value'] .= ' – ' . t('advanced mode');
          $items = array();
          $synced_files_db = variable_get(CDN_ADVANCED_SYNCED_FILES_DB_VARIABLE, FALSE);
          if ($synced_files_db !== FALSE) {
            $daemon_pid_file = str_replace(CDN_DAEMON_SYNCED_FILES_DB, CDN_DAEMON_PID_FILE, $synced_files_db);
            $persistent_data_db = str_replace(CDN_DAEMON_SYNCED_FILES_DB, CDN_DAEMON_PERSISTENT_DATA_DB, $synced_files_db);
            $drupal_root_path = variable_get(CDN_DRUPAL_ROOT_VARIABLE, realpath('.'));
            $synced_files_db_exists = file_exists($synced_files_db);
            $synced_files_db_readable = @fopen($synced_files_db, 'r');
            $persistent_data_db_exists = file_exists($persistent_data_db);
            $persistent_data_db_readable = @fopen($persistent_data_db, 'r');
            $daemon_pid_file_exists = file_exists($daemon_pid_file);
            $db = _cdn_advanced_get_db_connection();
            if ($db !== FALSE) {
              $input_file_mask = $drupal_root_path . '%';
              $sql = "SELECT COUNT(*) AS count, server FROM synced_files WHERE input_file LIKE :input_file GROUP BY server";
              $stmt = $db
                ->prepare($sql);
              $stmt
                ->bindParam(':input_file', $input_file_mask, PDO::PARAM_STR);
              $stmt
                ->execute();
              $result = $stmt
                ->fetchAll();
              $synced_file_stats = $result;
            }
            if ($persistent_data_db_exists && $persistent_data_db_readable) {
              try {
                $db = new PDO('sqlite:' . $persistent_data_db);
              } catch (PDOException $e) {
                $items[] = t("Could not connect to persistent data database.");
              }
              if ($db !== FALSE) {
                $sql = "SELECT COUNT(*) FROM pipeline_queue";
                $stmt = $db
                  ->prepare($sql);
                $stmt
                  ->execute();
                $result = $stmt
                  ->fetchAll();
                $pipeline_queue_count = $result[0][0];
                $sql = "SELECT COUNT(*) FROM pipeline_list";
                $stmt = $db
                  ->prepare($sql);
                $stmt
                  ->execute();
                $result = $stmt
                  ->fetchAll();
                $pipeline_list_count = $result[0][0];
              }
            }
            $items[] = $synced_files_db_exists ? t('The synced files database exists.') : t("The synced files database doesn't exist.");
            $items[] = $synced_files_db_readable ? t('The synced files database is readable.') : t("The synced files database isn't readable.");
            if ($synced_files_db_readable) {
              foreach ($synced_file_stats as $row) {
                $items[] = t('!synced-file-count files have been synced to the %server server.', array(
                  '!synced-file-count' => $row['count'],
                  '%server' => $row['server'],
                ));
              }
            }
            else {
              $items[] = t("Number of synced files is unknown.");
            }
            $items[] = $daemon_pid_file_exists ? t('The daemon is currently running.') : '<strong>' . t('The daemon is currently not running.') . '</strong>';
            if (isset($pipeline_queue_count)) {
              $items[] = t("!pipeline-queue-count files are waiting to be synced.", array(
                '!pipeline-queue-count' => $pipeline_queue_count,
              ));
              $items[] = t("!pipeline-list-count files are currently being synced.", array(
                '!pipeline-list-count' => $pipeline_list_count,
              ));
            }

            // If either of these 3 checks failed, mark this requirement's
            // severity as being an error.
            if (!($synced_files_db_exists && $synced_files_db_readable && $daemon_pid_file_exists)) {
              $requirements['cdn']['severity'] = REQUIREMENT_ERROR;
            }
          }
          else {
            $items[] = t('The synced files database setting has not yet been configured.');
            $requirements['cdn']['severity'] = REQUIREMENT_ERROR;
          }
          $requirements['cdn']['description'] .= '<br />' . theme('item_list', $items);
        }
      }
  }
  return $requirements;
}

//----------------------------------------------------------------------------

// Public functions.

/**
 * Gets the URL for a file when the basic mode is enabled.
 *
 * @param $path
 *   The path to get the URL for.
 */
function cdn_basic_get_url($path) {

  // Ensure the configuration is correct.
  if (variable_get(CDN_BASIC_URL_VARIABLE, FALSE) === FALSE) {
    return FALSE;
  }
  $extensions = str_replace(array(
    ' ',
    '.',
  ), array(
    '|',
    '\\.',
  ), variable_get(CDN_BASIC_EXTENSIONS_VARIABLE, CDN_BASIC_EXTENSIONS_DEFAULT));
  if (preg_match("/({$extensions})\$/", $path)) {
    $base_path = base_path();
    $cdn_url = variable_get(CDN_BASIC_URL_VARIABLE, 'http://yourcdn.com');
    return $cdn_url . $base_path . $path;
  }
  else {
    return FALSE;
  }
}

/**
 * Gets the URL for a file when the basic mode is enabled.
 *
 * @param $path
 *   The path to get the URL for.
 */
function cdn_advanced_get_url($path) {
  $db = _cdn_advanced_get_db_connection();

  // In case no connection to the database could be made, pretend the file was
  // not found in the synced files database.
  if (!$db) {
    return array(
      FALSE,
      FALSE,
    );
  }

  // Get the real path to the file (resolves symbolic links).
  $input_file = realpath('./' . $path);

  // Retrieve the URLs of the file on the CDN.
  $sql = "SELECT url, server FROM synced_files WHERE input_file = :input_file";
  $stmt = $db
    ->prepare($sql);
  $stmt
    ->bindParam(':input_file', $input_file, PDO::PARAM_STR);
  $stmt
    ->execute();
  $servers = $stmt
    ->fetchAll(PDO::FETCH_ASSOC);

  // The file is not available from any server.
  if (count($servers) == 0) {
    $url = FALSE;
    $server = FALSE;
  }
  else {
    if (count($servers) > 1 && function_exists('cdn_advanced_pick_server')) {
      $picked_server = cdn_advanced_pick_server($servers);
      $url = $picked_server['url'];
      $server = $picked_server['server'];
    }
    else {
      $url = $servers[0]['url'];
      $server = $servers[0]['server'];
    }
  }
  return array(
    $url,
    $server,
  );
}

//----------------------------------------------------------------------------

// Private functions.

/**
 * Get a connection to the database. The resulting PDO object is statically
 * cached.
 *
 * @return
 *   A database connection (through PDO), or FALSE in case of failure.
 */
function _cdn_advanced_get_db_connection() {
  static $db;
  $synced_files_db = variable_get(CDN_ADVANCED_SYNCED_FILES_DB_VARIABLE, FALSE);
  if ($synced_files_db === FALSE || !file_exists($synced_files_db) || filesize($synced_files_db) == 0) {
    $db = FALSE;
  }
  elseif (!isset($db)) {
    try {
      $db = new PDO('sqlite:' . variable_get(CDN_ADVANCED_SYNCED_FILES_DB_VARIABLE, ''));
    } catch (PDOException $e) {
      watchdog('cdn', t("Could not open synced files DB: %error.", array(
        '%error' => $e,
      )));
      $db = FALSE;
    }
  }
  return $db;
}

Functions

Namesort descending Description
cdn_advanced_get_url Gets the URL for a file when the basic mode is enabled.
cdn_basic_get_url Gets the URL for a file when the basic mode is enabled.
cdn_exit Implementation of hook_exit().
cdn_menu Implementation of hook_menu().
cdn_perm Implementation of hook_perm().
cdn_requirements Implementation of hook_requirements().
cdn_theme Implementation of hook_theme().
custom_file_url_rewrite
_cdn_advanced_get_db_connection Get a connection to the database. The resulting PDO object is statically cached.

Constants