diff adcache.inc @ 1:948362c2a207 ad

update advertisement
author pierre
date Thu, 02 Apr 2009 15:28:21 +0000
parents
children e5584a19768b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/adcache.inc	Thu Apr 02 15:28:21 2009 +0000
@@ -0,0 +1,568 @@
+<?php
+
+/**
+ * Wrapper for calling adserve_cache functions.
+ */
+function adserve_cache() {
+  static $functions = array();
+  $args = func_get_args();
+  $function = array_shift($args);
+
+  _debug_echo("adserve_cache function($function)");
+
+  if (!isset($functions[$function])) {
+    $cache = adserve_variable('adcache');
+    $test = "ad_cache_{$cache}_$function";
+    if (!function_exists($test)) {
+      _debug_echo("Cache function '$test' does not exist.\n");
+      $test = "adserve_cache_$function";
+    }
+    $functions[$function] = $test;
+  }
+
+  if (function_exists($functions[$function])) {
+    _debug_memory();
+    _debug_echo("Invoking cache function '". $functions[$function] ."'.");
+    return call_user_func_array($functions[$function], $args);
+  }
+  else {
+    _debug_echo("Cache function '". $functions[$function] ."' does not exist.\n");
+  }
+  return array();
+}
+
+/**
+ * Invoke adserve cache hook, including files as necessary.
+ */
+function adserve_invoke_hook() {
+  static $hooks = array();
+  $args = func_get_args();
+  $hook = array_shift($args);
+  $action = array_shift($args);
+
+  _debug_echo("adserve_invoke_hook hook($hook) action($action)");
+
+  if (!isset($hooks[$hook])) {
+    $hooks[$hook] = adserve_cache('hook', $hook);
+    if (is_array($hooks[$hook]) && !empty($hooks[$hook]) &&
+        is_array($hooks[$hook]['file'])) {
+      // Include all necessary files.
+      foreach ($hooks[$hook]['file'] as $files) {
+        if (is_array($files)) {
+          foreach ($files as $file) {
+            $include_file = adserve_variable('root_dir') .'/'. $file;
+            if (file_exists($include_file) && is_file($include_file)) {
+              _debug_echo("Including file: '$include_file'.");
+              include_once($include_file);
+            }
+            else {
+              _debug_echo("Failed to include file: '$include_file'.");
+            }
+          }
+        }
+      }
+    }
+  }
+
+  $return = array();
+  if (is_array($hooks[$hook]) && !empty($hooks[$hook]) &&
+      is_array($hooks[$hook]['function'])) {
+    foreach ($hooks[$hook]['function'] as $weight => $functions) {
+      foreach ($functions as $function) {
+        if (function_exists($function)) {
+          _debug_echo("Invoking '$function'.");
+          $return[] = call_user_func_array($function, $args);
+        }
+        else {
+          _debug_echo("Function '$function' does not exist.\n");
+        }
+      }
+    }
+  }
+  else {
+    $function = "adserve_hook_$hook";
+    if (function_exists($function)) {
+      _debug_echo("Invoking '$function'.");
+      $return[] = call_user_func_array($function, $args);
+    }
+    else {
+      _debug_echo("Function '$function' does not exist.\n");
+    }
+  }
+
+  switch ($action) {
+    case 'intersect':
+      if (sizeof($return) == 1) {
+        return $return[0];
+      }
+      else {
+        return call_user_func_array('array_intersect', $return);
+      }
+
+    case 'merge':
+      if (sizeof($return) == 1) {
+        return $return[0];
+      }
+      else {
+        $merge = array();
+        foreach ($return as $array) {
+          $merge += $array;
+        }
+        return $merge;
+      }
+
+    case 'first':
+      foreach ($return as $item) {
+        if (is_array($item) && !empty($item)) {
+          return $item;
+        }
+      }
+      return array();
+
+    case 'append':
+      $append = '';
+      foreach ($return as $item) {
+        if (!is_array($item)) {
+          $append .= $item;
+        }
+      }
+      return $append;
+
+    default:
+    case 'raw':
+    default:
+      return $return;
+  }
+}
+
+/** Cache functions **/
+
+/**
+ * Default initialization function, fully bootstraps Drupal to gain access to
+ * the database.
+ */
+function adserve_cache_open() {
+  adserve_bootstrap();
+}
+
+/**
+ * Build and return the cache.
+ * TODO: It's expensive to build the cache each time we serve an ad, this should
+ * be cached in the database, not in a static.
+ */
+function adserve_cache_get_cache($data = NULL) {
+  static $cache = NULL;
+  // if we don't the the cache yet, build it
+  if (is_null($cache)) {
+    $cache = module_invoke_all('ad_build_cache');
+  }
+
+  if ($data) {
+    if (isset($cache[$data])) {
+      return $cache[$data];
+    }
+    else {
+      return NULL;
+    }
+  }
+  return $cache;
+}
+
+/**
+ * Invoke the appropraite hook.
+ */
+function adserve_cache_hook($hook) {
+  static $cache = NULL;
+  // if we don't have the cache yet, build it
+  if (is_null($cache)) {
+    $external = adserve_cache('get_cache');
+    $cache = adserve_cache('build_hooks', $external);
+  }
+
+  // return hook definition, if exists
+  if (is_array($cache) && isset($cache["hook_$hook"]) && is_array($cache["hook_$hook"])) {
+    _debug_echo("Invoking hook '$hook'.");
+    return $cache["hook_$hook"];
+  }
+  _debug_echo("Did not find hook '$hook'.");
+}
+
+/**
+ * Helper function to build hook tree.
+ */
+function adserve_cache_build_hooks($cache) {
+  $return = array();
+  if (is_array($cache)) {
+    foreach ($cache as $module => $hooks) {
+      // supported cache hooks
+      foreach (array('hook_init', 'hook_filter', 'hook_weight', 'hook_select',
+                     'hook_init_text', 'hook_exit_text',
+                     'hook_increment_extra') as $hook) {
+        if (isset($hooks[$hook]) && is_array($hooks[$hook])) {
+          $weight = isset($hooks[$hook]['weight']) ? (int)$hooks[$hook]['weight'] : 0;
+          $return[$hook]['file'][$weight][] = $hooks[$hook]['file'];
+          $return[$hook]['function'][$weight][] = $hooks[$hook]['function'];
+        }
+      }
+    }
+  }
+  return $return;
+}
+
+/**
+ * Default function for retrieving list of ids.
+ */
+function adserve_cache_id($type, $id) {
+  switch ($type) {
+    case 'nids':
+      $result = db_query("SELECT aid FROM {ads} WHERE adstatus = 'active' AND aid IN(%d)", $id);
+      break;
+    case 'tids':
+      $result = db_query("SELECT a.aid FROM {ads} a INNER JOIN {term_node} n ON a.aid = n.nid WHERE a.adstatus = 'active' AND n.tid IN(%d)", $id);
+      break;
+    case 'default':
+      $result = db_query("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");
+      break;
+    default:
+      _debug_echo("Unsupported type '$type'.");
+  }
+
+  $ids = array();
+  if (isset($result)) {
+    while ($ad = db_fetch_object($result)) {
+      $ids[$ad->aid] = $ad->aid;
+    }
+  }
+  return $ids;
+}
+
+/**
+ * Support filter hooks.
+ */
+function adserve_hook_filter($ids, $hostid) {
+  return $ids;
+}
+
+/**
+ * Support weight hooks.
+ */
+function adserve_hook_weight($ids, $hostid) {
+  return $ids;
+}
+
+/**
+ * Load and display an advertisement directly from the database.
+ */
+function adserve_cache_display_ad($id) {
+  static $modules = array();
+
+  $ad = node_load($id);
+  if (!isset($modules[$ad->adtype])) {
+    $modules[$ad->adtype] = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s'", "ad_$ad->adtype"));
+  }
+  _debug_echo("Ad type '$ad->adtype', loading module '". $modules[$ad->adtype] ."'");
+  return module_invoke("ad_$ad->adtype", 'display_ad', $ad);
+}
+
+/**
+ * Validate aids.
+ */
+function adserve_cache_validate($aids, $displayed, $hostid) {
+  $valid = array();
+  foreach ($aids as $aid) {
+    if (!in_array($aid, $displayed)) {
+      $valid[] = $aid;
+    }
+  }
+  return $valid;
+}
+
+/**
+ * Increment action directly in the database.
+ */
+function adserve_cache_increment($action, $aid) {
+  $hostid = adserve_variable('hostid');
+  _debug_echo("adserve_increment action($action) aid($aid) hostid($hostid)");
+
+  // be sure that drupal is bootstrapped
+  adserve_bootstrap();
+
+  // allow add-on modules to implement their own statistics granularity
+  $extra = adserve_invoke_hook('increment_extra', 'merge', $action, $aid);
+  if (is_array($extra)) {
+    $extra = implode('|,|', $extra);
+  }
+  adserve_variable('extra', $extra);
+  _debug_echo("adserve_increment extra($extra)");
+
+  // update statistics
+  db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE aid = %d AND action = '%s' AND date = %d AND adgroup = '%s' AND extra = '%s' AND hostid = '%s'", $aid, $action, date('YmdH'), adserve_variable('group'), $extra, $hostid);
+  // if column doesn't already exist, add it
+  if (!db_affected_rows()) {
+    db_query("INSERT INTO {ad_statistics} (aid, date, action, adgroup, extra, hostid, count) VALUES(%d, %d, '%s', '%s', '%s', '%s', 1)", $aid, date('YmdH'), $action, adserve_variable('group'), $extra, $hostid);
+    if (!db_affected_rows()) {
+      // we lost a race to add it, increment it
+      db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE aid = %d AND action = '%s' AND date = %d AND adgroup = '%s' AND extra = '%s' AND hostid = '%s'", $aid, $action, date('YmdH'), adserve_variable('group'), $extra, $hostid);
+    }
+  }
+
+  if ($action == 'view') {
+    $ad = db_fetch_object(db_query('SELECT maxviews, activated FROM {ads} WHERE aid = %d', $aid));
+    // See if we need to perform additional queries.
+    if ($ad->maxviews) {
+      $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('autoexpired', $aid);
+        ad_statistics_increment('expired', $aid);
+      }
+    }
+  }
+}
+
+/**
+ * Randomly select advertisements.
+ * @param array, valid ad ids.
+ * @param integer, how many advertisements to select
+ * @param string, the hostid
+ */
+function adserve_hook_select($ids, $quantity = 1, $hostid = '') {
+  $select = 0;
+  $selected = array();
+  if (is_array($ids)) {
+    $ads = $ids;
+    foreach ($ids as $key => $value) {
+      $available = sizeof($ads);
+      $select++;
+      _debug_echo("Randomly selecting ad $select of $quantity.");
+      $id = 0;
+      if ($id == 0) {
+        $id = $available > 1 ? $ads[mt_rand(0, $available - 1)] : $ads[0];
+        _debug_echo("Randomly selected ID: $id.");
+        $selected[] = $id;
+        // strip away advertisments that have already been selected
+        $ads = adserve_cache('validate', $ads, array($id), $hostid);
+      }
+      if (($quantity == $select) || !count($ads)) {
+        // we have selected the right number of advertisements
+        break;
+      }
+    }
+  }
+  if ($select < $quantity) {
+    _debug_echo('No more advertisements available.');
+  }
+  return $selected;
+}
+/**
+ * Default wrapper function for displaying advertisements.  This generally
+ * is not replaced by ad caches modules.
+ */
+function adserve_cache_get_ad_ids() {
+  static $displayed_count = 0;
+  _debug_echo('Entering default adserve_display.');
+
+  // open the cache
+  adserve_cache('open');
+
+  $hostid = adserve_variable('hostid') ? adserve_variable('hostid') : 'none';
+  _debug_echo("Hostid: '$hostid'.");
+
+  // invoke hook_init
+  $init = adserve_invoke_hook('init', 'first', $hostid);
+
+  // start with list of advertisements provided externally
+  if (is_array($init) && !empty($init)) {
+    _debug_echo('Initialized externally.');
+    $quantity = $init['quantity'];
+    $id = $init['id'];
+    $aids = explode(',', $id);
+    $type = $init['type'];
+  }
+  else {
+    // build list of ad ids to choose from
+    $quantity = adserve_variable('quantity');
+    // use list for specific host
+    if ($ids = adserve_cache('id', 'host', NULL, $hostid)) {
+      $id = implode(',', $ids);
+      $type = 'host';
+    }
+    // use list of node ids
+    else if ($id = adserve_variable('nids')) {
+      $type = 'nids';
+      adserve_variable('group', "n$id");
+    }
+    // use list of group ids
+    else if ($id = adserve_variable('tids')) {
+      $type = 'tids';
+      adserve_variable('group', "t$id");
+    }
+    // use list without group ids
+    else {
+      $id = 0;
+      $type = 'default';
+      adserve_variable('group', "$id");
+    }
+    _debug_echo("Searching $type: $id");
+    $aids = adserve_cache('id', $type, $id, $hostid);
+  }
+
+  // prepare to select advertisements
+  $number_of_ads = sizeof($aids);
+  _debug_echo("Total ads: '$number_of_ads'.");
+
+  $displayed = adserve_variable("$type-displayed");
+  if (!is_array($displayed)) {
+    $displayed = array();
+  }
+  _debug_echo('Already displayed: '. sizeof($displayed));
+
+  // validate available advertisements
+  $aids = adserve_cache('validate', $aids, $displayed, $hostid);
+  $number_of_ads = sizeof($aids);
+  _debug_echo("Validated ads: '$number_of_ads'.");
+
+  // filter advertisements
+  $aids = adserve_invoke_hook('filter', 'intersect', $aids, $hostid);
+  $number_of_ads = sizeof($aids);
+  _debug_echo("Filtered ads: '$number_of_ads'.");
+
+
+  // apply weight to advertisements
+  $aids = adserve_invoke_hook('weight', 'first', $aids, $hostid);
+  $number_of_ads = sizeof($aids);
+  _debug_echo("Weighted ads: '$number_of_ads'.");
+
+  // select advertisements
+  $aids = adserve_invoke_hook('select', 'first', $aids, $quantity, $hostid);
+  $number_of_ads = sizeof($aids);
+  _debug_echo("Selected ads: '$number_of_ads'.");
+
+  // track which advertisements have been "displayed"
+  adserve_variable("$type-displayed", array_merge($aids, $displayed));
+
+  return $aids;
+}
+
+/**
+ * Default function for displaying advertisements.  This is not generally
+ * replaced by ad cache modules.
+ */
+function adserve_cache_display($ids) {
+  $output = '';
+  $ads = 0;
+  foreach ($ids as $id) {
+    $ad = adserve_cache('display_ad', $id);
+    _debug_echo('ad: '. htmlentities($ad));
+    // if displaying multiple ads, separate each with a div
+    if ($output) {
+      $group = adserve_variable('group');
+      $output .= "<div class=\"advertisement-space\" id=\"space-$group-$ads\"></div>";
+    }
+    // display advertisement
+    $output .= $ad;
+    // increment counters
+    if (adserve_variable('ad_display') == 'raw') {
+      $output .= ad_display_image($ad);
+    }
+    else {
+      adserve_cache('increment', 'view', $id);
+    }
+    $ads++;
+  }
+
+  if (empty($ids)) {
+    adserve_variable('error', TRUE);
+    $output = 'No active ads were found in '. adserve_variable('group');
+    adserve_cache('increment', 'count', NULL);
+  }
+
+  // close/update cache, if necessary
+  adserve_cache('close');
+
+  // update the dynamic portion of the output
+  $params = array();
+  $group = adserve_variable('group');
+  $replace = "/$group";
+  if ($hostid = adserve_variable('hostid')) {
+    $params[] = "hostid=$hostid";
+  }
+  if ($url = htmlentities(adserve_variable('url'))) {
+    $params[] = "url=$url";
+  }
+  if ($extra = adserve_variable('extra')) {
+    $params[] = "extra=$extra";
+  }
+  if (!empty($params)) {
+    $replace .= '?'. implode('&', $params);
+  }
+  $output = preg_replace('&/@HOSTID___&', $replace, $output);
+
+  // there was an error, hide the output in comments
+  if (adserve_variable('error')) {
+    $output = "<!-- $output -->";
+  }
+
+  // allow custom text to be displayed before and after advertisement
+  $init_text = adserve_invoke_hook('init_text', 'append');
+  $exit_text = adserve_invoke_hook('exit_text', 'append');
+  $output = $init_text . $output . $exit_text;
+
+  _debug_memory();
+
+  // TODO: turn all of these into hooks
+  switch (adserve_variable('ad_display')) {
+    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 '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');
+      }
+      else {
+        _debug_echo('Output: '. htmlentities($output));
+      }
+      print "$output";
+      exit(0);
+    case 'raw':
+      _debug_echo('Output: '. htmlentities($output));
+      chdir(adserve_variable('ad_dir'));
+      return $output;
+
+  }
+
+  _debug_echo('Output: '. htmlentities($output));
+  return $output;
+}
+