pierre@1: $functions) { pierre@1: foreach ($functions as $function) { pierre@1: if (function_exists($function)) { pierre@1: _debug_echo("Invoking '$function'."); pierre@1: $return[] = call_user_func_array($function, $args); pierre@1: } pierre@1: else { pierre@1: _debug_echo("Function '$function' does not exist.\n"); pierre@1: } pierre@1: } pierre@1: } pierre@1: } pierre@1: else { pierre@1: $function = "adserve_hook_$hook"; pierre@1: if (function_exists($function)) { pierre@1: _debug_echo("Invoking '$function'."); pierre@1: $return[] = call_user_func_array($function, $args); pierre@1: } pierre@1: else { pierre@1: _debug_echo("Function '$function' does not exist.\n"); pierre@1: } pierre@1: } pierre@1: pierre@1: switch ($action) { pierre@1: case 'intersect': pierre@1: if (sizeof($return) == 1) { pierre@1: return $return[0]; pierre@1: } pierre@1: else { pierre@1: return call_user_func_array('array_intersect', $return); pierre@1: } pierre@1: pierre@1: case 'merge': pierre@1: if (sizeof($return) == 1) { pierre@1: return $return[0]; pierre@1: } pierre@1: else { pierre@1: $merge = array(); pierre@1: foreach ($return as $array) { pierre@1: $merge += $array; pierre@1: } pierre@1: return $merge; pierre@1: } pierre@1: pierre@1: case 'first': pierre@1: foreach ($return as $item) { pierre@1: if (is_array($item) && !empty($item)) { pierre@1: return $item; pierre@1: } pierre@1: } pierre@1: return array(); pierre@1: pierre@1: case 'append': pierre@1: $append = ''; pierre@1: foreach ($return as $item) { pierre@1: if (!is_array($item)) { pierre@1: $append .= $item; pierre@1: } pierre@1: } pierre@1: return $append; pierre@1: pierre@1: default: pierre@1: case 'raw': pierre@1: default: pierre@1: return $return; pierre@1: } pierre@1: } pierre@1: pierre@1: /** Cache functions **/ pierre@1: pierre@1: /** pierre@1: * Default initialization function, fully bootstraps Drupal to gain access to pierre@1: * the database. pierre@1: */ pierre@1: function adserve_cache_open() { pierre@1: adserve_bootstrap(); pierre@1: } pierre@1: pierre@1: /** pierre@1: * Build and return the cache. pierre@1: * TODO: It's expensive to build the cache each time we serve an ad, this should pierre@1: * be cached in the database, not in a static. pierre@1: */ pierre@1: function adserve_cache_get_cache($data = NULL) { pierre@1: static $cache = NULL; pierre@1: // if we don't the the cache yet, build it pierre@1: if (is_null($cache)) { pierre@1: $cache = module_invoke_all('ad_build_cache'); pierre@1: } pierre@1: pierre@1: if ($data) { pierre@1: if (isset($cache[$data])) { pierre@1: return $cache[$data]; pierre@1: } pierre@1: else { pierre@1: return NULL; pierre@1: } pierre@1: } pierre@1: return $cache; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Invoke the appropraite hook. pierre@1: */ pierre@1: function adserve_cache_hook($hook) { pierre@1: static $cache = NULL; pierre@1: // if we don't have the cache yet, build it pierre@1: if (is_null($cache)) { pierre@1: $external = adserve_cache('get_cache'); pierre@1: $cache = adserve_cache('build_hooks', $external); pierre@1: } pierre@1: pierre@1: // return hook definition, if exists pierre@1: if (is_array($cache) && isset($cache["hook_$hook"]) && is_array($cache["hook_$hook"])) { pierre@1: _debug_echo("Invoking hook '$hook'."); pierre@1: return $cache["hook_$hook"]; pierre@1: } pierre@1: _debug_echo("Did not find hook '$hook'."); pierre@1: } pierre@1: pierre@1: /** pierre@1: * Helper function to build hook tree. pierre@1: */ pierre@1: function adserve_cache_build_hooks($cache) { pierre@1: $return = array(); pierre@1: if (is_array($cache)) { pierre@1: foreach ($cache as $module => $hooks) { pierre@1: // supported cache hooks pierre@1: foreach (array('hook_init', 'hook_filter', 'hook_weight', 'hook_select', pierre@1: 'hook_init_text', 'hook_exit_text', pierre@1: 'hook_increment_extra') as $hook) { pierre@1: if (isset($hooks[$hook]) && is_array($hooks[$hook])) { pierre@1: $weight = isset($hooks[$hook]['weight']) ? (int)$hooks[$hook]['weight'] : 0; pierre@1: $return[$hook]['file'][$weight][] = $hooks[$hook]['file']; pierre@1: $return[$hook]['function'][$weight][] = $hooks[$hook]['function']; pierre@1: } pierre@1: } pierre@1: } pierre@1: } pierre@1: return $return; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Default function for retrieving list of ids. pierre@1: */ pierre@1: function adserve_cache_id($type, $id) { pierre@1: switch ($type) { pierre@1: case 'nids': pierre@1: $result = db_query("SELECT aid FROM {ads} WHERE adstatus = 'active' AND aid IN(%d)", $id); pierre@1: break; pierre@1: case 'tids': pierre@1: $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); pierre@1: break; pierre@1: case 'default': pierre@1: $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"); pierre@1: break; pierre@1: default: pierre@1: _debug_echo("Unsupported type '$type'."); pierre@1: } pierre@1: pierre@1: $ids = array(); pierre@1: if (isset($result)) { pierre@1: while ($ad = db_fetch_object($result)) { pierre@1: $ids[$ad->aid] = $ad->aid; pierre@1: } pierre@1: } pierre@1: return $ids; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Support filter hooks. pierre@1: */ pierre@1: function adserve_hook_filter($ids, $hostid) { pierre@1: return $ids; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Support weight hooks. pierre@1: */ pierre@1: function adserve_hook_weight($ids, $hostid) { pierre@1: return $ids; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Load and display an advertisement directly from the database. pierre@1: */ pierre@1: function adserve_cache_display_ad($id) { pierre@1: static $modules = array(); pierre@1: pierre@1: $ad = node_load($id); pierre@1: if (!isset($modules[$ad->adtype])) { pierre@1: $modules[$ad->adtype] = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s'", "ad_$ad->adtype")); pierre@1: } pierre@1: _debug_echo("Ad type '$ad->adtype', loading module '". $modules[$ad->adtype] ."'"); pierre@1: return module_invoke("ad_$ad->adtype", 'display_ad', $ad); pierre@1: } pierre@1: pierre@1: /** pierre@1: * Validate aids. pierre@1: */ pierre@1: function adserve_cache_validate($aids, $displayed, $hostid) { pierre@1: $valid = array(); pierre@1: foreach ($aids as $aid) { pierre@1: if (!in_array($aid, $displayed)) { pierre@1: $valid[] = $aid; pierre@1: } pierre@1: } pierre@1: return $valid; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Increment action directly in the database. pierre@1: */ pierre@1: function adserve_cache_increment($action, $aid) { pierre@1: $hostid = adserve_variable('hostid'); sly@2: _debug_echo("adserve_cache_increment action($action) aid($aid) hostid($hostid)"); pierre@1: pierre@1: // be sure that drupal is bootstrapped pierre@1: adserve_bootstrap(); pierre@1: pierre@1: // allow add-on modules to implement their own statistics granularity pierre@1: $extra = adserve_invoke_hook('increment_extra', 'merge', $action, $aid); pierre@1: if (is_array($extra)) { pierre@1: $extra = implode('|,|', $extra); pierre@1: } pierre@1: adserve_variable('extra', $extra); sly@2: _debug_echo("adserve_cache_increment extra($extra)"); pierre@1: pierre@1: // update statistics pierre@1: 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); pierre@1: // if column doesn't already exist, add it pierre@1: if (!db_affected_rows()) { pierre@1: 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); pierre@1: if (!db_affected_rows()) { pierre@1: // we lost a race to add it, increment it pierre@1: 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); pierre@1: } pierre@1: } pierre@1: pierre@1: if ($action == 'view') { pierre@1: $ad = db_fetch_object(db_query('SELECT maxviews, activated FROM {ads} WHERE aid = %d', $aid)); pierre@1: // See if we need to perform additional queries. pierre@1: if ($ad->maxviews) { pierre@1: $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))); pierre@1: if ($views >= $ad->maxviews) { pierre@1: db_query("UPDATE {ads} SET adstatus = 'expired', autoexpire = 0, autoexpired = %d, expired = %d WHERE aid = %d", time(), time(), $aid); pierre@1: ad_statistics_increment('autoexpired', $aid); pierre@1: ad_statistics_increment('expired', $aid); pierre@1: } pierre@1: } pierre@1: } pierre@1: } pierre@1: pierre@1: /** pierre@1: * Randomly select advertisements. pierre@1: * @param array, valid ad ids. pierre@1: * @param integer, how many advertisements to select pierre@1: * @param string, the hostid pierre@1: */ pierre@1: function adserve_hook_select($ids, $quantity = 1, $hostid = '') { pierre@1: $select = 0; pierre@1: $selected = array(); pierre@1: if (is_array($ids)) { pierre@1: $ads = $ids; pierre@1: foreach ($ids as $key => $value) { pierre@1: $available = sizeof($ads); pierre@1: $select++; pierre@1: _debug_echo("Randomly selecting ad $select of $quantity."); pierre@1: $id = 0; pierre@1: if ($id == 0) { pierre@1: $id = $available > 1 ? $ads[mt_rand(0, $available - 1)] : $ads[0]; pierre@1: _debug_echo("Randomly selected ID: $id."); pierre@1: $selected[] = $id; pierre@1: // strip away advertisments that have already been selected pierre@1: $ads = adserve_cache('validate', $ads, array($id), $hostid); pierre@1: } pierre@1: if (($quantity == $select) || !count($ads)) { pierre@1: // we have selected the right number of advertisements pierre@1: break; pierre@1: } pierre@1: } pierre@1: } pierre@1: if ($select < $quantity) { pierre@1: _debug_echo('No more advertisements available.'); pierre@1: } pierre@1: return $selected; pierre@1: } pierre@1: /** pierre@1: * Default wrapper function for displaying advertisements. This generally pierre@1: * is not replaced by ad caches modules. pierre@1: */ pierre@1: function adserve_cache_get_ad_ids() { pierre@1: static $displayed_count = 0; pierre@1: _debug_echo('Entering default adserve_display.'); pierre@1: pierre@1: // open the cache pierre@1: adserve_cache('open'); pierre@1: pierre@1: $hostid = adserve_variable('hostid') ? adserve_variable('hostid') : 'none'; pierre@1: _debug_echo("Hostid: '$hostid'."); pierre@1: pierre@1: // invoke hook_init pierre@1: $init = adserve_invoke_hook('init', 'first', $hostid); pierre@1: pierre@1: // start with list of advertisements provided externally pierre@1: if (is_array($init) && !empty($init)) { pierre@1: _debug_echo('Initialized externally.'); pierre@1: $quantity = $init['quantity']; pierre@1: $id = $init['id']; pierre@1: $aids = explode(',', $id); pierre@1: $type = $init['type']; pierre@1: } pierre@1: else { pierre@1: // build list of ad ids to choose from pierre@1: $quantity = adserve_variable('quantity'); pierre@1: // use list for specific host pierre@1: if ($ids = adserve_cache('id', 'host', NULL, $hostid)) { pierre@1: $id = implode(',', $ids); pierre@1: $type = 'host'; pierre@1: } pierre@1: // use list of node ids pierre@1: else if ($id = adserve_variable('nids')) { pierre@1: $type = 'nids'; pierre@1: adserve_variable('group', "n$id"); pierre@1: } pierre@1: // use list of group ids pierre@1: else if ($id = adserve_variable('tids')) { pierre@1: $type = 'tids'; pierre@1: adserve_variable('group', "t$id"); pierre@1: } pierre@1: // use list without group ids pierre@1: else { pierre@1: $id = 0; pierre@1: $type = 'default'; pierre@1: adserve_variable('group', "$id"); pierre@1: } pierre@1: _debug_echo("Searching $type: $id"); pierre@1: $aids = adserve_cache('id', $type, $id, $hostid); pierre@1: } pierre@1: pierre@1: // prepare to select advertisements pierre@1: $number_of_ads = sizeof($aids); pierre@1: _debug_echo("Total ads: '$number_of_ads'."); pierre@1: pierre@1: $displayed = adserve_variable("$type-displayed"); pierre@1: if (!is_array($displayed)) { pierre@1: $displayed = array(); pierre@1: } pierre@1: _debug_echo('Already displayed: '. sizeof($displayed)); pierre@1: pierre@1: // validate available advertisements pierre@1: $aids = adserve_cache('validate', $aids, $displayed, $hostid); pierre@1: $number_of_ads = sizeof($aids); pierre@1: _debug_echo("Validated ads: '$number_of_ads'."); pierre@1: pierre@1: // filter advertisements pierre@1: $aids = adserve_invoke_hook('filter', 'intersect', $aids, $hostid); pierre@1: $number_of_ads = sizeof($aids); pierre@1: _debug_echo("Filtered ads: '$number_of_ads'."); pierre@1: pierre@1: pierre@1: // apply weight to advertisements pierre@1: $aids = adserve_invoke_hook('weight', 'first', $aids, $hostid); pierre@1: $number_of_ads = sizeof($aids); pierre@1: _debug_echo("Weighted ads: '$number_of_ads'."); pierre@1: pierre@1: // select advertisements pierre@1: $aids = adserve_invoke_hook('select', 'first', $aids, $quantity, $hostid); pierre@1: $number_of_ads = sizeof($aids); pierre@1: _debug_echo("Selected ads: '$number_of_ads'."); pierre@1: pierre@1: // track which advertisements have been "displayed" pierre@1: adserve_variable("$type-displayed", array_merge($aids, $displayed)); pierre@1: pierre@1: return $aids; pierre@1: } pierre@1: pierre@1: /** pierre@1: * Default function for displaying advertisements. This is not generally pierre@1: * replaced by ad cache modules. pierre@1: */ pierre@1: function adserve_cache_display($ids) { pierre@1: $output = ''; pierre@1: $ads = 0; pierre@1: foreach ($ids as $id) { pierre@1: $ad = adserve_cache('display_ad', $id); pierre@1: _debug_echo('ad: '. htmlentities($ad)); pierre@1: // if displaying multiple ads, separate each with a div pierre@1: if ($output) { pierre@1: $group = adserve_variable('group'); pierre@1: $output .= "
"; pierre@1: } pierre@1: // display advertisement pierre@1: $output .= $ad; pierre@1: // increment counters pierre@1: if (adserve_variable('ad_display') == 'raw') { sly@2: $output .= ad_display_image($id); pierre@1: } pierre@1: else { pierre@1: adserve_cache('increment', 'view', $id); pierre@1: } pierre@1: $ads++; pierre@1: } pierre@1: pierre@1: if (empty($ids)) { pierre@1: adserve_variable('error', TRUE); pierre@1: $output = 'No active ads were found in '. adserve_variable('group'); pierre@1: adserve_cache('increment', 'count', NULL); pierre@1: } pierre@1: pierre@1: // close/update cache, if necessary pierre@1: adserve_cache('close'); pierre@1: pierre@1: // update the dynamic portion of the output pierre@1: $params = array(); pierre@1: $group = adserve_variable('group'); pierre@1: $replace = "/$group"; pierre@1: if ($hostid = adserve_variable('hostid')) { pierre@1: $params[] = "hostid=$hostid"; pierre@1: } pierre@1: if ($url = htmlentities(adserve_variable('url'))) { pierre@1: $params[] = "url=$url"; pierre@1: } pierre@1: if ($extra = adserve_variable('extra')) { pierre@1: $params[] = "extra=$extra"; pierre@1: } pierre@1: if (!empty($params)) { pierre@1: $replace .= '?'. implode('&', $params); pierre@1: } pierre@1: $output = preg_replace('&/@HOSTID___&', $replace, $output); pierre@1: pierre@1: // there was an error, hide the output in comments pierre@1: if (adserve_variable('error')) { pierre@1: $output = ""; pierre@1: } pierre@1: pierre@1: // allow custom text to be displayed before and after advertisement pierre@1: $init_text = adserve_invoke_hook('init_text', 'append'); pierre@1: $exit_text = adserve_invoke_hook('exit_text', 'append'); pierre@1: $output = $init_text . $output . $exit_text; pierre@1: pierre@1: _debug_memory(); pierre@1: pierre@1: // TODO: turn all of these into hooks pierre@1: switch (adserve_variable('ad_display')) { pierre@1: case 'javascript': pierre@1: default: pierre@1: $output = str_replace(array("\r", "\n", "<", ">", "&"), pierre@1: array('\r', '\n', '\x3c', '\x3e', '\x26'), pierre@1: addslashes($output)); pierre@1: if (!adserve_variable('debug')) { pierre@1: // Tell the web browser not to cache this script so the ad refreshes pierre@1: // each time the page is viewed. pierre@1: // Expires in the past: pierre@1: header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); pierre@1: // Last load: pierre@1: header('Last-Modified: '. gmdate('D, d M Y H:i:s') .' GMT'); pierre@1: // HTTP 1.1: pierre@1: header('Cache-Control: no-store, no-cache, must-revalidate'); pierre@1: header('Cache-Control: post-check=0, pre-check=0', false); pierre@1: // HTTP 1.0: pierre@1: header('Pragma: no-cache'); pierre@1: // Output is a JavaScript: pierre@1: header('Content-Type: application/x-javascript; charset=utf-8'); pierre@1: } pierre@1: print "document.write('$output');"; pierre@1: exit(0); pierre@1: case 'iframe': pierre@1: case 'jquery': pierre@1: if (!adserve_variable('debug')) { pierre@1: // Tell the web browser not to cache this frame so the ad refreshes pierre@1: // each time the page is viewed. pierre@1: pierre@1: // Expires in the past: pierre@1: header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); pierre@1: // Last load: pierre@1: header('Last-Modified: '. gmdate('D, d M Y H:i:s') .' GMT'); pierre@1: // HTTP 1.1: pierre@1: header('Cache-Control: no-store, no-cache, must-revalidate'); pierre@1: header('Cache-Control: post-check=0, pre-check=0', false); pierre@1: // HTTP 1.0: pierre@1: header('Pragma: no-cache'); pierre@1: } pierre@1: else { pierre@1: _debug_echo('Output: '. htmlentities($output)); pierre@1: } pierre@1: print "$output"; pierre@1: exit(0); pierre@1: case 'raw': pierre@1: _debug_echo('Output: '. htmlentities($output)); pierre@1: chdir(adserve_variable('ad_dir')); pierre@1: return $output; pierre@1: pierre@1: } pierre@1: pierre@1: _debug_echo('Output: '. htmlentities($output)); pierre@1: return $output; pierre@1: } pierre@1: