diff adserve.inc @ 0:d8a3998dac8e ad

ajout module ad
author pierre
date Fri, 20 Feb 2009 14:04:09 +0000
parents
children 948362c2a207
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/adserve.inc	Fri Feb 20 14:04:09 2009 +0000
@@ -0,0 +1,732 @@
+<?php
+// $Id: adserve.inc,v 1.1.2.31.2.8 2009/02/17 19:22:45 jeremy Exp $
+
+/**
+ * @file
+ * Configuration.
+ *
+ * Copyright (c) 2005-2009.
+ *   Jeremy Andrews <jeremy@tag1consulting.com>.
+ *
+ * By default, adserve configuration happens dynamically as ads are served.
+ * However, it is possible to override dynamic settings with static defaults.
+ * Refer to the documentation/ADSERVE_CONFIGURATION.txt for details on adding
+ * adserve overrides to settings.php.
+ *
+ * Note that the path to Drupal's root directory can not be overriden in
+ * settings.php as adserve needs this path to find settings.php in the first
+ * place.  To hard code the path to Drupal's root directory, uncomment the
+ * following define statement, and set the correct path.  This is not generally
+ * required.  On a Unix server this path will be something like '/path/to/web'.
+ * On a Windows server this path will be something like 'D:\path\to\web'.
+ */
+//define('DRUPAL_ROOT', '/var/www/html');
+
+/**
+ * The main adserve logic.
+ */
+function adserve_ad($options = array()) {
+  static $displayed_count = 0;
+
+  // If no $options are passed in, assume we're using JavaScript.
+  if (!empty($options)) {
+    adserve_variable('variable_load', $options);
+  }
+  else {
+    adserve_variable('variable_load');
+  }
+  adserve_bootstrap(0);
+
+  adserve_debug();
+
+  adserve_variable('error', FALSE);
+  $output = NULL;
+  if (adserve_variable('adcache') != 'none') {
+    /**
+     * Ad caches are defined through external modules.  Ad caches are composed
+     * of a module 'ad_cache_TYPE.module' and an include file
+     * 'ad_cache_TYPE.inc' that live in the 'cache/TYPE' subdirectory where
+     * 'TYPE' is replaced with the type of cache.  For example, the included
+     * file cache lives in 'cache/file'.
+     *
+     * The ad_cache_TYPE.inc file must have a function named ad_cache_TYPE()
+     * which is used to display ads.  It can optionally include a function
+     * titled ad_cache_TYPE_variables used to extract any necessary
+     * variables from the global $_GET array (this can also be used to override
+     * values that would normally be set from $_GET).  Any functions used
+     * by this code without bootstrapping Drupal should also be in this file.
+     *
+     * The ad_cache_TYPE.module file should define the drupal _help() hook
+     * so the module can be enabled.  It should also define the _adcacheapi()
+     * hook allowing for configuration and processing.  Any functions used by
+     * this code after bootstrapping Drupal should also be in this module.
+     *
+     * Refer to cache/file/* for an implementation example.
+     */
+    $function = 'ad_cache_'. adserve_variable('adcache');
+    $output = adserve_invoke_file($function);
+
+  }
+
+  // If there's no output, we assume either there's no cache enabled, or the
+  // cache failed.
+  // TODO: Log failures with the watchdog.
+  if ($output == NULL) {
+    if (adserve_variable('debug')) {
+      echo "No cache enabled.<br />\n";
+    }
+
+    adserve_bootstrap();
+
+    if (adserve_variable('nids')) {
+      $id = adserve_variable('nids');
+      $type = 'nids';
+      adserve_variable('group', "n$id");
+
+      // Retrieve all active advertisements from the provided nid list.
+      $sql = "SELECT aid FROM {ads} WHERE adstatus = 'active' AND aid IN (%s)";
+      $result = db_query($sql, $id);
+
+      if (adserve_variable('debug')) {
+        echo "Searching for ad from nid list: $id.<br />\n";
+        echo "Query: \"$sql;\"<br />\n";
+      }
+    }
+    else if (adserve_variable('tids')) {
+      $id = adserve_variable('tids');
+      $type = 'tids';
+      adserve_variable('group', "t$id");
+
+      // Retrieve all active advertisements from the provided tid list.
+      $sql = "SELECT a.aid FROM {ads} a INNER JOIN {term_node} n ON a.aid = n.nid WHERE a.adstatus = 'active' AND n.tid IN (%s)";
+      $result = db_query($sql, $id);
+
+      if (adserve_variable('debug')) {
+        echo "Searching for ad from tid list: $id.<br />\n";
+        echo "Query: \"$sql;\"<br />\n";
+      }
+    }
+    else {
+      $id = 0;
+      $type = 'default';
+      adserve_variable('group', "$id");
+
+      // Randomly determine which ad to display from those that do not have
+      // any tid assigned to them.
+      $sql = "SELECT a.aid FROM {ads} a LEFT JOIN {term_node} n ON a.aid = n.nid WHERE a.adstatus = 'active' AND n.tid IS NULL";
+      $result = db_query($sql);
+
+      if (adserve_variable('debug')) {
+        echo "Searching for ads with no tids.<br />\n";
+        echo "Query: \"$sql;\"<br />\n";
+      }
+    }
+
+    // Build list of all available ads to choose from.
+    $available = array();
+    while ($ad = db_fetch_object($result)) {
+      $available[$ad->aid] = $ad->aid;
+    }
+    if (adserve_variable('debug')) {
+      echo 'Available ads: ';
+      if (sizeof($ads)) {
+        echo implode(', ', $available) ."<br />";
+      }
+      else {
+        echo 'none<br />';
+      }
+    }
+
+    // Randomly select from available advertisements.
+    $selected = adserve_select_ad($available, adserve_variable('quantity'));
+
+    $output = '';
+    $ads = 0;
+    $details = array();
+    $ids = array();
+    // Include appropriate module for displaying selected ad.
+    foreach ($selected as $aid) {
+      $ids[$aid] = $aid;
+      $ads++;
+      $detail = $details[$aid] = node_load($aid);
+      if (!isset($modules[$detail->adtype])) {
+        $modules[$detail->adtype] = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s'", 'ad_'. $detail->adtype));
+      }
+      if (adserve_variable('debug')) {
+        echo 'ad: <pre>';
+        print_r($detail);
+        echo '</pre>';
+        echo "Loading module '". $modules[$detail->adtype] ."'.<br />\n";
+      }
+      include_once $modules[$detail->adtype];
+
+      if ($output) {
+        // Add a div between ads that themers can use to arrange ads when
+        // displaying more than one at a time.
+        $displayed_count++;
+        $output .= "<div class=\"advertisement-space\" id=\"space-$id-$displayed_count\"></div>";
+      }
+      $output .= module_invoke("ad_$detail->adtype", 'display_ad', $detail);
+
+      // Update the ad's impressions counter.
+      if (adserve_variable('ad_display') == 'raw') {
+        $output .= ad_display_image($detail);
+      }
+      else {
+        adserve_increment($detail);
+      }
+    }
+    adserve_variable("$type-ids", $ids);
+    if (empty($ads)) {
+      adserve_variable('error', TRUE);
+      $output = 'No active ads were found in the '. (empty($nids) ? 'tids' : 'nids') ." '$id'.";
+      adserve_increment(NULL, 'count');
+    }
+    if (adserve_variable('debug')) {
+      echo "Ads displayed: $ads<br />";
+    }
+  }
+
+  $hostid = adserve_variable('hostid');
+  $group = adserve_variable('group');
+  $replace = "/$group";
+  if (!empty($hostid)) {
+    $replace .= "/$hostid";
+  }
+  if ($url = htmlentities(adserve_variable('url'))) {
+    $replace .= "?u=$url";
+  }
+
+  $output = preg_replace('&/@HOSTID___&', $replace, $output);
+  if (adserve_variable('error')) {
+    $output = "<!-- $output -->";
+  }
+
+  /**
+   * Modules can add custom code to be displayed before or after ads are
+   * displayed.  For example, you many want to add a tagline, "Powered by
+   * Drupal".  To do so, define 'adserve_exit_text' within your module's
+   * adapi hook.
+   *
+   * Code sample for adserve_exit_text example:
+   *
+   *   sample_adapi($op, $ad) {
+   *     case 'adserve_exit_text':
+   *       return array(
+   *         'sample' => array(
+   *           'text' => t('Powered by Drupal'),
+   *         )
+   *       );
+   *   }
+   *
+   * As another example use case, you could also use the _init_text and
+   * _exit_text hooks to wrap all advertisements in a custom div.
+   */
+  $init = TRUE;
+  foreach (array('adserve_init_text', 'adserve_exit_text') as $hook) {
+    $result = adserve_invoke_hook($hook);
+    if (is_array($result)) {
+      $append = '';
+      foreach ($result as $text) {
+        if ($text['text']) {
+          $append .= $text['text'];
+        }
+      }
+      if ($init) {
+        $output = $append . $output;
+      }
+      else {
+        $output .= $append;
+      }
+    }
+    $init = FALSE;
+  }
+
+  switch (adserve_variable('ad_display')) {
+    case 'iframe':
+    case 'jquery':
+      if (!adserve_variable('debug')) {
+        // Tell the web browser not to cache this frame so the ad refreshes
+        // each time the page is viewed.
+
+        // Expires in the past:
+        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+        // Last load:
+        header('Last-Modified: '. gmdate('D, d M Y H:i:s') .' GMT');
+        // HTTP 1.1:
+        header('Cache-Control: no-store, no-cache, must-revalidate');
+        header('Cache-Control: post-check=0, pre-check=0', FALSE);
+        // HTTP 1.0:
+        header('Pragma: no-cache');
+      }
+      print "$output";
+      exit(0);
+    case 'javascript':
+    default:
+      $output = str_replace(array("\r", "\n", "<", ">", "&"),
+                            array('\r', '\n', '\x3c', '\x3e', '\x26'),
+                            addslashes($output));
+      if (!adserve_variable('debug')) {
+        // Tell the web browser not to cache this script so the ad refreshes
+        // each time the page is viewed.
+        // Expires in the past:
+        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
+        // Last load:
+        header('Last-Modified: '. gmdate('D, d M Y H:i:s') .' GMT');
+        // HTTP 1.1:
+        header('Cache-Control: no-store, no-cache, must-revalidate');
+        header('Cache-Control: post-check=0, pre-check=0', FALSE);
+        // HTTP 1.0:
+        header('Pragma: no-cache');
+        // Output is a JavaScript:
+        header('Content-Type: application/x-javascript; charset=utf-8');
+      }
+      print "document.write('$output');";
+      exit(0);
+    case 'raw':
+      chdir(adserve_variable('ad_dir'));
+      return $output;
+  }
+}
+
+/**
+ * Retrieve variables from $_GET array or from passed in $value array.
+ */
+function adserve_variable($variable, $value = NULL) {
+  global $conf;
+  static $variables = NULL, $overridden = NULL, $cache_loaded = array();
+
+  // Update the value, if set.
+  if (isset($value)) {
+    $variables->$variable = $value;
+  }
+
+  if (!isset($variables->loaded) || $variable == 'variable_load') {
+    if ($variable == 'variable_load' && isset($value)) {
+      $values['debug'] = isset($value['debug']) ? $value['debug'] : '';
+      $values['c'] = isset($value['adcache']) ? $value['adcache'] : '';
+      $values['n'] = isset($value['nids']) ? $value['nids'] : '';
+      $values['t'] = isset($value['tids']) ? $value['tids'] : '';
+      $values['k'] = isset($value['hostid']) ? $value['hostid'] : '';
+      $values['q'] = isset($value['quantity']) ? $value['quantity'] : 1;
+      $values['m'] = isset($value['ad_display']) ? $value['ad_display'] : 0;
+      unset($value);
+    }
+    else {
+      $values = $_GET;
+    }
+
+    // Don't use getcwd as path may involve symbolic links
+    $variables->ad_dir = dirname($_SERVER['SCRIPT_FILENAME']);
+    // 'debug' is an integer.
+    $variables->debug = isset($values['debug']) ? (int)$values['debug'] : 0;
+    // Cache types are comprised of only letters.
+    $variables->adcache = isset($values['c']) ? preg_replace('/[^a-zA-Z]/', '', $values['c']) : 'none';
+    // Nids is an integer or a ",".
+    $variables->nids = isset($values['n']) ? preg_replace('/[^0-9,]/', '', $values['n']) : '';
+    // Tids is an integer or a ",".
+    $variables->tids = isset($values['t']) ? preg_replace('/[^0-9,]/', '', $values['t']) : '';
+    // Hostid is an md5() which is comprised of numbers and letters a-f.
+    $variables->hostid = isset($values['k']) ? preg_replace('/[^0-9a-f]/', '', $values['k']) : '';
+    // Click url
+    $variables->url = isset($values['u']) ? $values['u'] : '';
+    // Quantity is an integer.
+    $variables->quantity = isset($values['q']) ? (int)$values['q'] : 0;
+    // Ad ID is an integer.
+    $variables->aid = isset($values['a']) ? (int)$values['a'] : 0;
+    // Method is compriese of only letters.
+    $variables->ad_display = isset($values['m']) ? preg_replace('/[^a-zA-Z]/', '', $values['m']) : 'javascript';
+
+    // Set defaults.
+    $variables->quantity = $variables->quantity ? $variables->quantity : 1;
+
+    if ($variables->debug) {
+      foreach ($variables as $variable => $val) {
+        echo "$variable: '$val'<br />\n";
+      }
+      if ($variables->debug == 1) exit;
+    }
+    $variables->loaded = TRUE;
+
+    // Override the value, if set during initialization.
+    if (isset($value)) {
+      $variables->$variable = $value;
+    }
+  }
+
+  if (!$overridden) {
+    if (isset($conf)) {
+      foreach ($conf as $var => $val) {
+        $variables->$var = $val;
+        if ($variables->debug) {
+          echo "Override $var: '$val'<br />\n";
+        }
+      }
+      $overridden = TRUE;
+    }
+  }
+
+  if (!isset($cache_loaded[$variables->adcache])) {
+    // Retrieve variables defined by cache plugin, if enabled.
+    if ($variables->adcache != 'none') {
+      $include = $variables->ad_dir ."/cache/$variables->adcache/ad_cache_$variables->adcache.inc";
+      if (file_exists($include)) {
+        if ($variables->debug) {
+          echo "Attempting to include cache include file '$include'.<br />\n";
+        }
+        require_once($include);
+      }
+      else if ($variables->debug) {
+        echo "Failed to find cache include file '$include'.<br />\n";
+      }
+      $function = 'ad_cache_'. $variables->adcache .'_variables';
+      if (function_exists($function)) {
+        $external_variables = $function();
+        foreach ($external_variables as $key => $val) {
+          if (!isset($variables->$key)) {
+            $variables->$key = $val;
+          }
+        }
+      }
+    }
+    $cache_loaded[$variables->adcache] = TRUE;
+  }
+
+  if ($variable == 'variable_dump') {
+    echo "Dumping \$variables:<br />\n";
+    echo '<pre>';
+    foreach ($variables as $var => $val) {
+      echo "  $var($val)<br />\n";
+    }
+    echo '</pre>';
+  }
+
+  if (isset($variables->$variable)) {
+    return $variables->$variable;
+  }
+  else {
+    return NULL;
+  }
+}
+
+/**
+ * Invoke a function in the specified file.
+ */
+function adserve_invoke_file($function, $arg1 = NULL, $arg2 = NULL) {
+  $output = '';
+  if (function_exists($function)) {
+    $output = $function($arg1, $arg2);
+  }
+  else if (adserve_variable('debug')) {
+    echo "Function '$function' does not exist.<br />\n";
+  }
+  return $output;
+}
+
+/**
+ * Invoke adserve hooks, defined in adapi with adserve_HOOK.
+ */
+function adserve_invoke_hook($hook, $a1 = NULL, $a2 = NULL) {
+  if (adserve_variable('adcache') != 'none') {
+    $cache = adserve_variable('adcache');
+    _debug_echo("Invoking adserve hook '$hook' in $cache cache.");
+    // Get information from cache.
+    return adserve_invoke_file("ad_cache_{$cache}_$hook", $a1, $a2);
+  }
+  else {
+    _debug_echo("Invoking adserve hook '$hook'.");
+    // Get information from Drupal variable table.
+    $actions = variable_get($hook, '');
+    $return = array();
+    if (!empty($actions)) {
+      $actions = unserialize($actions);
+      foreach ($actions as $name => $action) {
+        if ($action['function']) {
+          $function = $action['function'];
+          if (!function_exists($function)) {
+            if ($action['path']) {
+              _debug_echo("Including file '". $action['path'] ."'.");
+              include_once($action['path']);
+            }
+          }
+          if (function_exists($function)) {
+            _debug_echo("Invoking function '$function'.");
+            $return[] = $function($a1, $a2);
+          }
+          else if (adserve_variable('debug')) {
+            echo "Function '$function' does not exist.<br />\n";
+          }
+        }
+        else {
+          $return[] = $action;
+        }
+      }
+    }
+    return $return;
+  }
+  // Retreive hook definition from cache if using, or from variable_get
+  // return hook action.
+}
+
+function _debug_echo($text) {
+  if (adserve_variable('debug')) {
+    echo "$text<br />\n";
+  }
+}
+
+/**
+ * Remove one or more ids from array.
+ * TODO: Optimize.  Perhaps something like array_flip, unset, array_flip.
+ * @param $ids An array of ID's, ie array(5, 8, 9, 11).
+ * @param $remove An ID or an array of ID's to be removed from $ids.
+ */
+function adserve_select_reindex($ids, $remove) {
+  $new = array();
+  // Walk through array of IDs and decide what to keep.
+  foreach ($ids as $id) {
+    // If $remove is an array, walk through array to decide fate of ID.
+    if (is_array($remove)) {
+      $keep = TRUE;
+      foreach ($remove as $rem) {
+        // Loop until we find one that matches or reach end of array.
+        if ($id == $rem) {
+          $keep = FALSE;
+          break;
+        }
+      }
+      if ($keep) {
+        $new[] = $id;
+      }
+    }
+    else {
+      if ($id != $remove) {
+        $new[] = $id;
+      }
+    }
+  }
+  return $new;
+}
+
+/**
+ * Disabled: will be re-implemented with new adserve hooks introduced for
+ * geotargeting.
+function adserve_invoke_weight($ads, $quantity = 1, $invalid = array()) {
+  $parent = adserve_variable('ad_dir') .'/weight';
+  if (is_dir($parent) && $handle = opendir($parent)) {
+    while ($dir = readdir($handle)) {
+      if (is_dir("$parent/$dir") && !in_array($dir, array('.', '..', 'CVS'))) {
+        $include = "$parent/$dir/ad_weight_$dir.inc";
+        if (file_exists($include)) {
+          require_once($include);
+          $function = "ad_weight_{$dir}_select_ad";
+          if (function_exists($function)) {
+            $return = $function($ads, $quantity, $invalid);
+            // First come, first serve.  We found an ad_weight function that
+            // returned something, so we'll take it.
+            if ($return) {
+              return $return;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+ */
+
+/**
+ * Simple default function to randomly select an ad.  Provides a hook to allow
+ * the definition of external display methods.
+ * @param An array of valid ad IDs, ie array(5, 8, 9, 11).
+ * @param Optional, how many unique ads to select.
+ * @param Optional, an array of invalid IDs.
+ */
+function adserve_select_ad($ads, $quantity = 1, $invalid = array()) {
+  //adserve_invoke_weight($ads, $quantity, $invalid);
+
+  $ids = array();
+  $id = 0;
+  $total = sizeof($ads);
+  _debug_echo("Selecting $quantity ad(s) from $total total ad(s).");
+  if (is_array($ads)) {
+    $ads = adserve_select_reindex($ads, $invalid);
+    $total = sizeof($ads);
+    for ($i = 0; $i < $quantity; $i++) {
+      _debug_echo('Randomly selecting ad: '. ($i + 1) ." of $quantity.");
+      $id = 0;
+      // Randomly select a unique banner to display.  We subtract 1 as arrays
+      // start at 0.
+      $return = adserve_invoke_hook('adserve_select', $ads, $invalid);
+      if (is_array($return) && !empty($return)) {
+        foreach ($return as $id) {
+          // First come first serve.
+          if ((int)$id) break;
+        }
+      }
+      if ($id >= 0 && sizeof($ads)) {
+        if ($id == 0) {
+          _debug_echo("Default ID selection in adserve.inc.");
+          $id = $total > 1 ? $ads[mt_rand(0, $total - 1)] : $ads[0];
+          _debug_echo("Randomly selected ID: $id.");
+        }
+        if ($id > 0) {
+          $ids[] = $id;
+        }
+      }
+      else {
+        // There are no more valid advertisements left to display.
+        break;
+      }
+      $invalid[] = $id;
+      $ads = adserve_select_reindex($ads, $id);
+      $total = sizeof($ads);
+      // We're out of ads to display.
+      if ($total <= 0) {
+        break;
+      }
+    }
+  }
+  return $ids;
+}
+
+/**
+ * Include Drupal's bootstrap.inc.
+ */
+function adserve_include_drupal() {
+  // For optimal performance set DRUPAL_ROOT at the top of this file.
+  if (defined('DRUPAL_ROOT')) {
+    if (is_dir(DRUPAL_ROOT) && file_exists(DRUPAL_ROOT .'/includes/bootstrap.inc')) {
+      chdir(DRUPAL_ROOT);
+      adserve_variable('root_dir', DRUPAL_ROOT);
+    }
+    else {
+      echo 'Invalid DRUPAL_ROOT ('. DRUPAL_ROOT .') defined in adserve.inc';
+    }
+  }
+  else {
+    $path = explode('/', adserve_variable('ad_dir'));
+    while (!empty($path)) {
+      // Search for top level Drupal directory to perform bootstrap.
+      chdir(implode('/', $path));
+      if (file_exists('./includes/bootstrap.inc')) {
+        adserve_variable('root_dir', getcwd());
+        break;
+      }
+      array_pop($path);
+    }
+  }
+  require_once adserve_variable('root_dir') .'/includes/bootstrap.inc';
+}
+
+/**
+ * Include the necessary files and call the Drupal bootstrap.
+ */
+function adserve_bootstrap($bootstrap = NULL) {
+  adserve_include_drupal();
+
+  // If no specific bootstrap is specified, do a full bootstrap.
+  if (!isset($bootstrap)) {
+    $bootstrap = DRUPAL_BOOTSTRAP_FULL;
+  }
+
+  if (adserve_variable('debug')) {
+    echo "Drupal bootstrap '". $bootstrap ."'.<br />\n";
+  }
+
+  drupal_bootstrap($bootstrap);
+}
+
+/**
+ * Increment ad counters.  Increment in cache if enabled.
+ */
+function adserve_increment($ad, $action = 'view') {
+  $cache = adserve_variable('adcache');
+  if (adserve_variable('debug')) {
+    echo "adserve_increment action($action) cache($cache)<br />\n";
+  }
+  if (is_object($ad) && isset($ad->aid)) {
+    $aid = $ad->aid;
+  }
+  else {
+    $aid = 0;
+  }
+  if ($cache != 'none') {
+    $rc = adserve_invoke_file("ad_cache_{$cache}_increment", $action, $aid);
+    if ($rc) return;
+  }
+  adserve_bootstrap();
+  // Update impressions statistics.
+  db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE aid = %d AND action = '%s' AND date = %d AND adgroup = '%s' AND hostid = '%s'", $aid, $action, date('YmdH'), adserve_variable('group'), adserve_variable('hostid'));
+  // If column doesn't already exist, we need to add it.
+  if (!db_affected_rows()) {
+    db_query("INSERT INTO {ad_statistics} (aid, date, action, adgroup, hostid, count) VALUES(%d, %d, '%s', '%s', '%s', 1)", $aid, date('YmdH'), $action, adserve_variable('hostid'), adserve_variable('hostid'));
+    // If another process already added this row our INSERT will fail, if
+    // so we still need to increment it so we don't loose an impression.
+    if (!db_affected_rows()) {
+      db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE aid = %d AND action = '%s' AND date = %d AND adgroup = '%s' AND hostid = '%s'", $aid, $action, date('YmdH'), adserve_variable('group'), adserve_variable('hostid'));
+    }
+  }
+
+  if ($action == 'view') {
+    // See if we need to perform additional queries.
+    if (isset($ad->maxviews) && $ad->maxviews > 0) {
+      $views = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d", $aid, date('YmdH', $ad->activated)));
+      if ($views >= $ad->maxviews) {
+        db_query("UPDATE {ads} SET adstatus = 'expired', autoexpire = 0, autoexpired = %d, expired = %d WHERE aid = %d", time(), time(), $aid);
+        ad_statistics_increment($aid, 'autoexpired');
+        ad_statistics_increment($aid, 'expired');
+      }
+    }
+  }
+  // TODO: Do we need to do this here?  Can it happen when a new click is
+  // registered?
+  if (isset($ad->maxclicks) && $ad->maxclicks > 0) {
+    $clicks = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d", $aid, date('YmdH', $ad->activated)));
+    if ($clicks >= $ad->maxclicks) {
+      db_query("UPDATE {ads} SET adstatus = 'expired', autoexpire = 0, autoexpired = %d, expired = %d WHERE aid = %d", time(), time(), $aid);
+      ad_statistics_increment($aid, 'autoexpired');
+      ad_statistics_increment($aid, 'expired');
+    }
+  }
+}
+
+/**
+ * Display additional debug information.
+ */
+function adserve_debug() {
+  if (adserve_variable('debug')) {
+    echo "Root drupal directory detected as '". adserve_variable('root_dir') ."'.<br />\n<br />\n";
+
+    $ad_dir = adserve_variable('ad_dir');
+    $files = array("$ad_dir/serve.php", "$ad_dir/ad.module");
+    if (adserve_variable('debug') > 2) {
+      $files = array_merge($files, array("$ad_dir/ad.install"));
+    }
+    if (adserve_variable('debug') > 3) {
+      $files = array_merge($files, array("$ad_dir/image/ad_image.module", "$ad_dir/image/ad_image.install", "$ad_dir/text/ad_text.module", "$ad_dir/text/ad_text.install", "$ad_dir/embed/ad_embed.module", "$ad_dir/report/ad_report.module", "$ad_dir/notify/ad_notify.module", "$ad_dir/notify/ad_notify.install"));
+    }
+    foreach ($files as $file) {
+      if (!file_exists($file)) {
+        echo "Error: '$file' does not exist!<br />\n";
+      }
+      else if (!is_readable($file)) {
+        echo "Error: '$file' is not readable!<br />\n";
+      }
+      else {
+        $fd = fopen($file, 'r');
+        while (!feof($fd)) {
+          $line = fgets($fd);
+          if (substr($line, 0, 5) == "<?php") {
+            continue;
+          }
+          else {
+            echo "$file: $line<br />";
+            break;
+          }
+        }
+      }
+    }
+    echo "<br />\n";
+  }
+}
+