You are here

ad.module in Advertisement 6.2

File

ad.module
View source
<?php

/**
 * @file
 * An advertising system for Drupal powered websites.
 *
 * Copyright (c) 2005-2009.
 *   Jeremy Andrews <jeremy@tag1consulting.com>.
 */
require_once 'ad_token.inc';

/**
 * Implementation of hook_theme().
 */
function ad_theme() {
  return array(
    'ad_display' => array(
      'file' => 'ad.module',
      'arguments' => array(
        'group' => NULL,
        'display' => NULL,
        'method' => 'javascript',
      ),
    ),
    'ad_status_display' => array(
      'file' => 'ad.module',
      'arguments' => array(
        'node' => NULL,
      ),
    ),
    'ad_statistics_display' => array(
      'file' => 'ad.pages.inc',
      'arguments' => array(
        'statistics' => NULL,
      ),
    ),
    'node_ad' => array(
      'file' => 'ad.pages.inc',
      'arguments' => array(
        'node' => NULL,
        'yield_form' => TRUE,
      ),
    ),
    'ad_admin_ads' => array(
      'file' => 'ad.admin.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'ad_filters' => array(
      'file' => 'ad.admin.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'ad_filter_form' => array(
      'file' => 'ad.admin.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
}

/**
 * Use this function to display ads from a specified group.
 *
 * @param $group
 *  The ad group tid to display ads from.
 * @param $quantity
 *  Optionally specify the number of unique ads to display.
 * @param $options
 *  Any number of options from this list:  hostid, nids.
 */
function ad($group = FALSE, $quantity = 1, $options = array()) {
  global $base_url;
  $adserve = variable_get('adserve', '');
  $adserveinc = variable_get('adserveinc', '');
  if (empty($adserve) || empty($adserveinc)) {

    // This is probably the first time ad() has been called.
    _ad_check_installation();
    $adserve = variable_get('adserve', '');
    $adserveinc = variable_get('adserveinc', '');
  }
  if (!file_exists($adserve) || !file_exists($adserveinc)) {
    drupal_set_message(t('Ads cannot be displayed.  The ad module is <a href="@misconfigured">misconfigured</a>, failed to locate the required <em>serve.php</em> ond/or <em>adserve.inc</em> file.', array(
      '@misconfigured' => url('admin/content/ad/configure'),
    )), 'error');
    _ad_check_installation();
    return t('The ad module is <a href="@misconfigured">misconfigured</a>.', array(
      '@misconfigured' => url('admin/content/ad/configure'),
    ));
  }

  // Be sure a display method has been chosen.
  if (!isset($options['ad_display'])) {
    $options['ad_display'] = variable_get('ad_display', 'javascript');
  }
  $options['quantity'] = isset($quantity) ? $quantity : 1;
  if (!isset($options['tids'])) {
    $options['tids'] = $group;
  }
  $options['cache'] = variable_get('ad_cache', 'none');
  switch ($options['ad_display']) {
    case 'raw':
      require_once drupal_get_path('module', 'ad') . '/adserve.inc';
      require_once drupal_get_path('module', 'ad') . '/adcache.inc';
      $output = adserve_ad($options);
      break;
    case 'iframe':
    case 'jquery':
      $query['m'] = $options['ad_display'];

    // Fall through...
    case 'javascript':
    default:
      $query['q'] = $quantity;
      if (isset($options['hostid'])) {
        $query['k'] = $options['hostid'];
      }

      // Allow external cache files to define additional display variables.
      if ($options['cache'] != 'none') {
        $query['c'] = $options['cache'];
        $cache_variables = module_invoke('ad_cache_' . $options['cache'], 'adcacheapi', 'display_variables', array());
        if (is_array($cache_variables)) {
          foreach ($cache_variables as $key => $value) {
            $query[$key] = $value;
          }
        }
      }

      // Allow ad_type modules to define additional display variables.
      $type_variables = module_invoke_all('adapi', 'display_variables', array());
      if (is_array($type_variables)) {
        foreach ($type_variables as $key => $value) {
          $query[$key] = $value;
        }
      }
      if (isset($options['nids'])) {

        // Choose ads from the provided list of node Id's.
        $nids = $options['nids'];
        $query['n'] = $nids;
        $group = "nids-{$nids}";
      }
      else {
        if (isset($options['tids'])) {

          // Choose ads from the provided list of taxonomy terms.
          $tids = $options['tids'];
          $query['t'] = $tids;
          $group = "tids-{$tids}";
        }
        else {

          // Choose ads from the specified group.
          $query['t'] = $group;
          $options['tids'] = $group;
        }
      }
      if (isset($options['url'])) {
        $query['u'] = $options['url'];
      }
      else {
        $query['u'] = $_GET['q'];
        if ($alias = drupal_get_path_alias($_GET['q'])) {
          $query['l'] = $alias;
        }
      }
      $src = url($base_url . '/' . $adserve, array(
        'query' => $query,
      ));
      if ($options['ad_display'] == 'iframe') {

        // TODO: We need to know the IFrame size before it is displayed.  This
        // limits the flexibility of what can be displayed in these frames.
        // For now we'll have a global value, later we'll add per-group
        // over-rides.
        $attributes['frameborder'] = variable_get('ad_iframe_frameborder', 0);
        $attributes['scrolling'] = variable_get('ad_iframe_scroll', 'auto');
        $attributes['name'] = $group;
        if ($height = variable_get('ad_iframe_height', '')) {
          $attributes['height'] = $height;
        }
        if ($width = variable_get('ad_iframe_width', '')) {
          $attributes['width'] = $width;
        }
        $output = '<iframe src="' . htmlentities($src) . '"' . drupal_attributes($attributes) . "></iframe>";
      }
      else {
        if ($options['ad_display'] == 'jquery') {

          // The theme function uses this to generate a CSS id for jQuery to use.
          $output = $src;
        }
        else {
          $output = "<script type='text/javascript' src='" . htmlentities($src) . "'></script>";
        }
      }
      break;
  }
  if (user_access('show advertisements')) {
    return theme('ad_display', $group, $output, $options['ad_display']);
  }
  else {
    return theme('ad_display', 'none', "<!-- Enable 'show advertisements' permission if you wish to display ads here. -->");
  }
}

/**
 * Function to display the actual advertisement to the screen.  Wrap it in a
 * theme function to make it possible to customize in your own theme.
 */
function theme_ad_display($group, $display, $method = 'javascript') {
  static $id = -1;

  // Increment counter for displaying multiple advertisements on the page.
  $id++;

  // The naming convention for the id attribute doesn't allow commas.
  $group = preg_replace('/[,]/', '+', $group);
  if ($method == 'jquery') {
    return "\n<div class=\"advertisement group-{$group}\" id=\"group-id-{$id}\">\n <script type=\"text/javascript\">\n//<![CDATA[\n  \$(document).ready(function(){ jQuery(\"div#group-id-{$id}\").load(\"{$display}\"); });\n //]]>\n </script>\n</div>\n";
  }
  else {
    if ($method == 'raw') {
      return $display;
    }
    else {
      return "\n<div class=\"advertisement group-{$group}\" id=\"group-id-{$group}\">{$display}</div>\n";
    }
  }
}

/**
 * Update click counter then redirect host to ad's target URL.
 */
function ad_redirect($aid, $group = NULL) {
  global $user;
  $hostid = isset($_GET['hostid']) ? $_GET['hostid'] : '';
  $extra = isset($_GET['extra']) ? $_GET['extra'] : '';
  if (function_exists('click_filter_status')) {
    $status = click_filter_status($aid, $hostid);
  }
  else {
    $status = 0;
  }

  // Allow source url to be passed in.
  $url = isset($_GET['u']) ? $_GET['u'] : '';
  if (!isset($url) || !valid_url($url)) {
    $url = referer_uri();
  }
  db_query("INSERT INTO {ad_clicks} (aid, uid, status, hostname, user_agent, adgroup, extra, hostid, url, timestamp) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d)", $aid, $user->uid, $status, ip_address(), $_SERVER['HTTP_USER_AGENT'], $group, $extra, $hostid, $url, time());
  watchdog('ad', 'Clicked %type ad aid %aid hostid %hostid.', array(
    '%type' => $adtype,
    '%aid' => $aid,
    '%hostid' => $hostid,
  ));
  if (function_exists('click_filter_status') && $status == CLICK_VALID) {
    ad_statistics_increment($aid, 'click', $group, $hostid);
  }
  else {
    if (!function_exists('click_filter_status')) {

      // We're not filtering clicks, so all clicks are valid.
      ad_statistics_increment($aid, 'click', $group, $hostid);
    }
  }

  // Determine where we're supposed to redirect the user.
  $adtype = db_result(db_query('SELECT adtype FROM {ads} WHERE aid = %d', $aid));
  $node->nid = $node->aid = $aid;
  $node->hostid = $hostid;
  $url = module_invoke('ad_' . $adtype, 'adapi', 'redirect', $node);
  if (isset($url)) {
    header('Location: ' . $url);
  }
  else {
    watchdog('ad', 'Ad redirection failed for aid %aid hostid %hostid, failed to load destination URL. ', array(
      '%aid' => $aid,
      '%hostid' => $hostid,
    ));
    drupal_goto('');
  }
}

/**
 * Ad API Helper Function:
 * Append all necessary attributes to <a> tags.
 */
function ad_link_attributes() {
  return array_merge(ad_link_target(TRUE), ad_link_nofollow(TRUE));
}

/**
 * Ad API Helper Function:
 * Provide XHTML-strict-compatible target window onclick-handlers based on
 * global configuration.
 */
function ad_link_target() {
  switch (variable_get('ad_link_target', '_self')) {
    case '_blank':
      $target = array(
        'onclick' => 'window.open(this.href); return false;',
      );
      break;
    case '_parent':
      $target = array(
        'onclick' => 'window.parent.location = this.href; return false;',
      );
      break;
    case '_top':
      $target = array(
        'onclick' => 'window.top.location = this.href; return false;',
      );
      break;
    default:
      $target = array();
      break;
  }
  return $target;
}

/**
 * Force the cache to be flushed.
 */
function ad_rebuild_cache($verbose = FALSE) {
  $cache = variable_get('ad_cache', 'none');
  $build = "ad_cache_{$cache}_build";
  if (function_exists($build)) {
    if ($verbose) {
      drupal_set_message(t('Rebuilding ad cache.'));
    }
    $build();
  }
}

/**
 * Ad API Helper Function:
 * Append rel="nofollow" if globally enabled.
 */
function ad_link_nofollow() {
  if (variable_get('ad_link_nofollow', 0)) {
    $nofollow = array(
      'rel' => 'nofollow',
    );
  }
  else {
    $nofollow = array();
  }
  return $nofollow;
}

/**
 * Increment action counter.
 */
function ad_statistics_increment($aid, $action, $group = NULL, $hostid = NULL) {

  // Update action statistics.
  db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE date = %d AND aid = %d AND action = '%s' AND adgroup = '%s' AND hostid = '%s'", date('YmdH'), $aid, $action, $group, $hostid);

  // If column doesn't already exist, we need to add it.
  if (!db_affected_rows()) {
    db_query("INSERT INTO {ad_statistics} (aid, adgroup, hostid, date, action, count) VALUES(%d, '%s', '%s', %d, '%s', 1)", $aid, $group, $hostid, date('YmdH'), $action);

    // 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 action.
    if (!db_affected_rows()) {
      db_query("UPDATE {ad_statistics} SET count = count + 1 WHERE date = %d AND aid = %d AND action = '%s' AND adgroup = '%s' AND hostid = '%s'", date('YmdH'), $aid, $action, $group, $hostid);
    }
  }
  $event = array(
    'aid' => $aid,
    'action' => $action,
    'hostid' => $hostid,
  );
  module_invoke_all('adapi', 'statistics_increment', $event);
}

/**
 * Return an array with all status values user has permission to set.
 * A user with 'administer advertisements' permission can update any status.
 */
function ad_status_array($aid = 0, $status = NULL) {
  $permissions = array();

  // mark status as pending
  if (user_access('administer advertisements') || $status == 'pending' || $status == NULL || ad_permission($aid, 'set status as pending')) {
    $permissions['pending'] = t('This advertisement is currently waiting for administrative approval.');
  }

  // mark status from pending to approved
  if (user_access('administer advertisements') || $status == 'approved' || $status == 'pending' && ad_permission($aid, 'set status from pending to approved')) {
    $permissions['approved'] = t('This advertisement has been approved and is currently waiting to be activated.');
  }

  // mark status as active (from pending, approved, or offline)
  if (user_access('administer advertisements') || $status == 'active' || $status == 'approved' && ad_permission($aid, 'set status from approved to active') || $status == 'offline' && ad_permission($aid, 'set status from offline to active')) {
    $permissions['active'] = t('This advertisement is actively being displayed.');
  }

  // mark status as offline (from pending, approved, or active)
  if (user_access('administer advertisements') || $status == 'offline' || $status == 'approved' && ad_permission($aid, 'set status from approved to offline') || $status == 'active' && ad_permission($aid, 'set status from active to offline')) {
    $permissions['offline'] = t('This advertisement has been temporarily disabled by its owner and is not currently being displayed.');
  }

  // mark status as expired (from active or offline)
  if (user_access('administer advertisements') || $status == 'expired' || $status == 'active' && ad_permission($aid, 'set status from active to expired') || $status == 'offline' && ad_permission($aid, 'set status from offline to expired')) {
    $permissions['expired'] = t('This advertisement has expired and is no longer being displayed.');
  }

  // mark status as denied (from pending or any)
  if (user_access('administer advertisements') || $status == 'denied' || $status == 'pending' && ad_permission($aid, 'set status from pending to denied') || ad_permission($aid, 'set status as denied')) {
    $permissions['denied'] = t('This advertisement was refused by the site administrator and will not be displayed.');
  }
  return $permissions;
}

/**
 * Display the status of the currently viewed ad.
 */
function theme_ad_status_display($node) {
  if (isset($node->adstatus)) {
    $status_array = ad_status_array($node->nid, $node->adstatus);
    $output = '<div class="adstatus">';
    $output .= '<p>' . t($status_array[$node->adstatus]) . '</p>';
    switch ($node->adstatus) {
      case 'approved':
        if ($node->autoactivate) {
          $output .= '<p>' . t('This advertisement will be automatically activated on %timestamp, in %time.', array(
            '%timestamp' => format_date($node->autoactivate, 'large'),
            '%time' => format_interval($node->autoactivate - time()),
          )) . '</p>';
        }
        break;
      case 'active':
        $activated = db_result(db_query("SELECT activated FROM {ads} WHERE aid = %d", $node->nid));
        if ($activated) {
          $output .= '<p>' . t('This advertisement has been active since %date.', array(
            '%date' => format_date($activated, 'large'),
          )) . '</p>';
        }
        if ($node->autoexpire) {
          $output .= '<p>' . t('This advertisement will expire on %timestamp, in %time.', array(
            '%timestamp' => format_date($node->autoexpire, 'large'),
            '%time' => format_interval($node->autoexpire - time()),
          )) . '</p>';
        }
        if ($node->maxviews) {
          $views = (int) db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d", $node->nid, date('YmdH', $node->activated)));
          $output .= '<p>' . t('This advertisement will expire after %left more impressions.', array(
            '%left' => $node->maxviews - $views,
          )) . '</p>';
        }
        if ($node->maxclicks) {
          $clicks = (int) db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d", $node->nid, date('YmdH', $node->activated)));
          $output .= '<p>' . t('This advertisement will expire after %left more clicks.', array(
            '%left' => $node->maxclicks - $clicks,
          )) . '</p>';
        }
        break;
      case 'expired':
        $expired = db_result(db_query("SELECT expired FROM {ads} WHERE aid = %d", $node->nid));
        if ($expired) {
          $output .= '<p>' . t('This advertisement expired %date.', array(
            '%date' => format_date($expired, 'large'),
          )) . '</p>';
        }
        break;
    }
    $output .= '</div>';
    return theme('box', t('Status'), $output);
  }
}

/**
 * Implementation of hook_init.
 */
function ad_init() {

  // We do this in the init hook so that it doesn't get skipped when block
  // caching is enabled.
  $method = variable_get('ad_display', 'javascript');
  if ($method == 'jquery') {
    drupal_add_js('misc/jquery.js', 'core');
  }
}

/**
 * Implementation of hook_help().
 */
function ad_help($path, $arg) {
  $output = '';
  switch ($path) {
    case 'admin/help#ad':
      $output = '<p>' . t('The ad module provides a complete advertising system for Drupal powered websites.  It does this through an API that allow other modules to handle various types of advertising content.  For example, if enabled together with the ad_image module you will be able to display image based advertisements such as banner ads.') . '</p>';
      break;
    case 'node/add/ad':
      $output = '<p>' . t('Advertisements can be randomly displayed to visitors of your website.') . '</p>';
      break;
  }
  return $output;
}

/**
 * Implementation of hook_cron().
 */
function ad_cron() {
  if (time() - variable_get('ad_cron_timestamp', 0) >= 60) {

    // Locate ads that need to be activated or expired.
    $result = db_query('SELECT aid, adstatus, adtype, autoactivate, autoactivated, autoexpire, autoexpired FROM {ads} WHERE autoactivate <> 0 OR autoexpire <> 0');
    while ($ad = db_fetch_object($result)) {
      switch ($ad->adstatus) {
        case 'approved':

          // See if this ad is ready to be activated.
          if ($ad->autoactivate && $ad->autoactivate <= time()) {
            $node = node_load($ad->aid);

            // Activate the ad.
            db_query("UPDATE {ads} SET adstatus = 'active', autoactivate = 0, autoactivated = %d, activated = %d WHERE aid = %d", time(), time(), $ad->aid);
            ad_statistics_increment($ad->aid, 'autoactivated');
            ad_statistics_increment($ad->aid, 'active');
            watchdog('ad', 'Automatically activated ad %title with nid %nid.', array(
              '%title' => $node->title,
              '%nid' => $node->nid,
            ));

            // Allow modules to do special processing to automatically
            // activated advertisements.
            module_invoke('ad_' . $ad->adtype, 'adapi', 'autoactivate', $node);
          }
          else {
            if (!$ad->autoactivate) {

              // Once daily warn that there's an ad stuck in approved state.
              if (time() - variable_get("ad_autoactivate_warning_{$ad->aid}", 0) >= 8600) {
                watchdog('ad', 'Warning: ad %title with nid %nid in approved state has no autoactivate date set.', array(
                  '%title' => $node->title,
                  '%nid' => $node->nid,
                ));
                variable_set("ad_autoactivate_warning_{$ad->aid}", time());
              }
            }
          }
          break;
        case 'active':

          // See if this ad is ready to be activated.
          if ($ad->autoexpire && $ad->autoexpire <= time()) {
            $node = node_load($ad->aid);

            // Expire the ad.
            db_query("UPDATE {ads} SET adstatus = 'expired', autoexpire = 0, autoexpired = %d, expired = %d WHERE aid = %d", time(), time(), $ad->aid);
            ad_statistics_increment($ad->aid, 'autoexpired');
            ad_statistics_increment($ad->aid, 'expired');
            watchdog('ad', 'Automatically expired ad %title with nid %nid.', array(
              '%title' => $node->title,
              '%nid' => $node->nid,
            ));

            // Allow modules to do special processing to automatically
            // activated advertisements.
            module_invoke('ad_' . $ad->adtype, 'adapi', 'autoexpire', $node);
          }
          else {
            if (!$ad->autoexpire) {

              // Ad is already activated, but has autoactivate timestamp set.
              db_query("UPDATE {ads} SET autoactivate = 0 WHERE aid = %d", $ad->aid);
            }
          }
          break;
        default:
          $node = node_load($ad->aid);
          db_query('UPDATE {ads} SET autoactivate = 0, autoexpire = 0 WHERE aid = %d', $ad->aid);
          watchdog('ad', 'Warning: reset %type timestamp on advertisement %title with nid %nid because it is in %state state.', array(
            '%title' => $node->title,
            '%nid' => $node->nid,
            '%type' => $ad->autoactivate ? 'autoactivate' : 'autoexpire',
            '%state' => $ad->adstatus,
          ));
      }
    }
    variable_set('ad_cron_timestamp', time());
  }
}

/**
 * Implementation of hook_perm().
 */
function ad_perm() {
  return array(
    'administer advertisements',
    'create advertisements',
    'edit own advertisements',
    'edit any advertisement',
    'delete own advertisements',
    'delete any advertisement',
    'show advertisements',
  );
}

/**
 * Implementation of hook_node_info().
 */
function ad_node_info() {
  $items['ad'] = array(
    'name' => t('Advertisement'),
    'module' => 'ad',
    'description' => t('Advertisements can be randomly displayed to visitors of your website.'),
  );
  return $items;
}

/**
 * Implementation of hook_access().
 */
function ad_access($op, $node, $account) {
  switch ($op) {
    case 'create':
      return user_access('create advertisements', $account) || user_access('administer advertisements');
    case 'update':
      return user_access('edit any advertisement', $account) || user_access('edit own advertisements', $account) && is_ad_owner($node->nid) || user_access('administer advertisements', $account);
    case 'delete':
      return user_access('delete any advertisement', $account) || user_access('delete own advertisements', $account) && is_ad_owner($node->nid) || user_access('administer advertisements', $account);
    case 'view':

      // Return false if user doesn't have basic permissions to view
      // advertisements.  Don't return true to let default Drupal
      // domain/node access checks happen.
      if (!user_access('show advertisements', $account) && !user_access('administer advertisements', $account)) {
        return false;
      }
  }
}

/**
 * Implementation of hook_form().
 */
function ad_form(&$node, &$form_state) {
  $form = array();
  $type = node_get_types('type', $node);

  // When form_state is not empty, we should rather use it's values
  // to not loose data if validation fails.
  if (!empty($form_state['values'])) {
    $node = (object) $form_state['values'];
  }
  $form['aid'] = array(
    '#type' => 'value',
    '#value' => isset($node->nid) ? $node->nid : 0,
  );
  if ($type->has_title) {
    $form['title'] = array(
      '#type' => 'textfield',
      '#title' => check_plain($type->title_label),
      '#required' => TRUE,
      '#default_value' => isset($node->title) ? $node->title : '',
      '#weight' => -5,
    );
  }
  if ($type->has_body) {
    $form['body_field'] = node_body_field($node, $type->body_label, $type->min_word_count);
  }
  else {

    // The body array has to exist otherwise the format field gets removed.
    $form['body'] = array(
      '#type' => 'hidden',
      '#value' => '',
    );
    $form['body_field']['format'] = filter_form($node->format);
  }

  // determine the current ad type
  if (!isset($adtype)) {
    $adtypes = ad_get_types();
    if (count($adtypes) == 1) {
      $adtype = key($adtypes);
    }
    else {
      if (!count($adtypes)) {
        drupal_set_message(t('At least one ad type module must be enabled before you can create advertisements.  For example, try <a href="!url">enabling</a> the ad_text or ad_image module.', array(
          '!url' => url('admin/build/modules'),
        )), 'error');
      }
    }
  }

  // display ad type switch
  if ((!isset($node->adtype) || isset($node->adtype_select)) && count($adtypes) > 1) {
    $adtypes = array(
      0 => '---',
    );
    $adtypes += ad_get_types('name');
    $form['select'] = array(
      '#type' => 'fieldset',
      '#title' => t('Select Ad type'),
      '#prefix' => '<div class="container-inline">',
      '#suffix' => '</div>',
      '#weight' => 3,
    );
    $form['select']['adtype_select'] = array(
      '#type' => 'select',
      '#required' => TRUE,
      '#options' => $adtypes,
      '#default_value' => isset($node->adtype_select) ? $node->adtype_select : '',
      '#ahah' => array(
        'path' => 'node/add/ad/ahah',
        'wrapper' => 'adtype-ahah-wrapper',
        'effect' => 'slide',
      ),
    );
  }

  // display type-specific options
  if (isset($node->adtype) && $node->adtype || count($adtypes) == 1) {
    if (isset($node->adtype_select) && $node->adtype_select && $node->adtype_select != $node->adtype) {
      $node->adtype = $node->adtype_select;
    }
    if (count($adtypes) == 1) {
      $node->adtype = $adtype;
    }
    ad_form_add_adtype_elements($form, $node->adtype, $node);

    // add ahah wrapper
    $form['adtype_elements']['#prefix'] = '<div id="adtype-ahah-wrapper">';
    $form['adtype_elements']['#suffix'] = '</div>';
  }
  if (!isset($form['adtype_elements'])) {
    $form['adtype_elements'] = array(
      '#value' => '<div id="adtype-ahah-wrapper"></div>',
      '#weight' => 3.1,
    );
  }

  // fieldset for updating ad status
  $form['adstatus'] = array(
    '#type' => 'fieldset',
    '#title' => t('Status'),
    '#collapsible' => TRUE,
    '#weight' => 4,
  );
  $nid = isset($node->nid) ? $node->nid : 0;
  $adstatus = isset($node->adstatus) ? $node->adstatus : '';

  // display all available status options
  foreach (ad_status_array($nid, $adstatus) as $status => $description) {
    $form['adstatus']["ad{$status}"] = array(
      '#type' => 'radio',
      '#title' => t("{$status}"),
      '#return_value' => $status,
      '#default_value' => isset($node->adstatus) ? $node->adstatus : 'pending',
      '#description' => "{$description}",
      '#parents' => array(
        "adstatus",
      ),
    );
  }

  // display scheduling options
  $form['schedule'] = array(
    '#type' => 'fieldset',
    '#title' => t('Scheduling'),
    '#collapsible' => TRUE,
    // Collapse if there isn't any scheduling data set.
    '#collapsed' => isset($node->autoactivate) || isset($form_state['values']['autoactivate']) || isset($node->autoexpire) || isset($form_state['values']['autoexpire']) || isset($node->maxviews) || isset($form_state['values']['maxviews']) || isset($node->maxclicks) || isset($form_state['values']['maxclicks']) ? FALSE : TRUE,
  );
  $date_api = FALSE;
  if (module_exists('date_popup')) {
    $date_api = TRUE;
  }
  if (isset($node->nid) && ad_permission($node->nid, 'manage status') || user_access('administer advertisements')) {
    if (!$date_api) {
      $form['schedule']['current'] = array(
        '#type' => 'markup',
        '#prefix' => '<div>',
        '#suffix' => '</div>',
        '#value' => t('The current date and time is "%date".', array(
          '%date' => format_date(time(), 'custom', 'F j, Y H:i'),
        )),
      );
    }
    $form['schedule']['autoactivate'] = array(
      '#type' => $date_api ? 'date_popup' : 'textfield',
      '#title' => t('Automatically activate ad'),
      '#required' => FALSE,
      '#description' => t('You can specify a date and time for this advertisement to be automatically activated.  The advertisement needs to be in an <em>approved</em> state before it can be automatically activated.  If you prefer to activate the advertisement immediately, leave this field empty.'),
    );
    if ($date_api) {
      $form['schedule']['autoactivate']['#date_format'] = 'F j, Y H:i';
      $form['schedule']['autoactivate']['#default_value'] = isset($node->autoactivate) && $node->autoactivate > 0 ? format_date((int) $node->autoactivate, 'custom', 'Y-m-j H:i') : '';
    }
    else {
      $form['schedule']['autoactivate']['#default_value'] = isset($node->autoactivate) && $node->autoactivate > 0 ? format_date((int) $node->autoactivate, 'custom', 'F j, Y H:i') : 0;
    }
  }
  if (user_access('administer advertisements')) {

    // admins can expire advertisements
    $form['schedule']['autoexpire'] = array(
      '#type' => $date_api ? 'date_popup' : 'textfield',
      '#title' => t('Automatically expire ad'),
      '#required' => FALSE,
      '#description' => t('You can specify a date and time for this advertisement to be automatically expired.  If you don\'t want the advertisement to expire, leave this field empty.'),
    );
    if ($date_api) {
      $form['schedule']['autoexpire']['#date_format'] = 'F j, Y H:i';
      $form['schedule']['autoexpire']['#default_value'] = isset($node->autoexpire) && $node->autoexpire > 0 ? format_date((int) $node->autoexpire, 'custom', 'Y-m-j H:i') : '';
    }
    else {
      $form['schedule']['autoexpire']['#default_value'] = isset($node->autoexpire) && $node->autoexpire > 0 ? format_date((int) $node->autoexpire, 'custom', 'F j, Y H:i') : 0;
    }
    $form['schedule']['maxviews'] = array(
      '#type' => 'textfield',
      '#title' => t('Maximum impressions'),
      '#required' => FALSE,
      '#size' => 10,
      '#maxlength' => 11,
      '#default_value' => isset($node->maxviews) ? $node->maxviews : 0,
      '#description' => t('You can specify the maximum number of times this advertisement should be displayed, after which it will be automatically expired.  If you don\'t want this advertisement to expire after a certain number of impressions, leave this field set to %zero.', array(
        '%zero' => '0',
      )),
    );
    $form['schedule']['maxclicks'] = array(
      '#type' => 'textfield',
      '#title' => t('Maximum clicks'),
      '#required' => FALSE,
      '#size' => 10,
      '#maxlength' => 11,
      '#default_value' => isset($node->maxclicks) ? $node->maxclicks : 0,
      '#description' => t('You can specify the maximum number of times this advertisement should be clicked, after which it will be automatically expired.  If you don\'t want this advertisement to expire after a certain number of clicks leave this field set to %zero.', array(
        '%zero' => '0',
      )),
    );
  }
  else {

    // display expiration time
    $form['schedule']['autoexpire_display'] = array(
      '#type' => 'markup',
      '#prefix' => '<div>',
      '#suffix' => '</div>',
      '#value' => theme('ad_status_display', $node),
    );
    $form['schedule']['autoexpire'] = array(
      '#type' => 'hidden',
      '#value' => isset($node->autoexpire) ? $node->autoexpire : 0,
    );
  }
  $form['#validate'][] = 'ad_select_adtype';
  return $form;
}

/**
 * Ad type switch submit handler.
 */
function ad_select_adtype(&$form, &$form_state) {
  if (!$form_state['values']['adtype'] && !$form_state['values']['adtype_select']) {
    form_set_error('adtype_select', t('Please, select an Ad type.'));
  }
  if (!isset($form_state['values']['adtype']) || isset($form_state['values']['adtype_select']) && $form_state['values']['adtype'] != $form_state['values']['adtype_select']) {
    $form_state['values']['adtype'] = $form_state['values']['adtype_select'];
    $form_state['rebuild'] = TRUE;
  }
}

/**
 * Ad type switch AHAH menu handler.
 */
function ad_form_ahah() {
  $form_state = array(
    'submitted' => FALSE,
  );
  $form_build_id = $_POST['form_build_id'];
  if (!($form = form_get_cache($form_build_id, $form_state))) {
    exit;
  }
  ad_form_add_adtype_elements($form, $_POST['adtype_select']);
  form_set_cache($form_build_id, $form, $form_state);
  $form += array(
    '#post' => $_POST,
    '#programmed' => FALSE,
  );

  // Rebuild the form.
  $form = form_builder($_POST['form_id'], $form, $form_state);
  $option_form = $form['adtype_elements'];
  unset($option_form['#prefix'], $option_form['#suffix']);
  $output = drupal_render($option_form);
  drupal_json(array(
    'status' => TRUE,
    'data' => $output,
  ));
}

/**
 * Loads Ad type elements into form.
 */
function ad_form_add_adtype_elements(&$form, $adtype, $node = NULL) {
  unset($form['adtype_elements']);
  $form['adtype_elements'] = module_invoke('ad_' . $adtype, 'adapi', 'form', $node);
  $form['adtype'] = array(
    '#type' => 'hidden',
    '#value' => $adtype,
  );
  $form['adtype_elements']['#weight'] = 3.1;
}

/**
 * Implementation of hook_form_alter().
 */
function ad_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'taxonomy_form_vocabulary') {

    // Remove taxonomy form options not applicable for ad groups.
    if ($form['vid']['#value'] == _ad_get_vid()) {
      $form['help_ad_vocab'] = array(
        '#value' => t('This vocabulary was automatically created for use by the ad module.  Only applicable options are available.'),
        '#weight' => -1,
      );
      $form['nodes']['ad'] = array(
        '#type' => 'checkbox',
        '#title' => t('ad group'),
        '#value' => 1,
        '#attributes' => array(
          'disabled' => '',
        ),
        '#description' => t('Type %type is required to use this vocabulary.', array(
          '%type' => t('ad group'),
        )),
      );
      $form['tags']['#description'] = t('If enabled, ads are categorized by typing ad group names instead of choosing them from a list.');
      $form['multiple']['#description'] = t('If enabled, allows ads to have more than one ad group (always true for free tagging).');
      $form['required']['#description'] = t('If enabled, every ad <strong>must</strong> be assigned to at least one ad group.');
      $form['hierarchy'] = array(
        '#type' => 'value',
        '#value' => 0,
      );
      unset($form['relations']);
    }
    else {
      unset($form['nodes']['ad']);
    }
  }
  else {
    if ($form_id == 'taxonomy_form_term') {
      if ($form['vid']['#value'] == _ad_get_vid()) {
        $form['name']['#title'] = t('Ad group name');
        $form['name']['#description'] = t('The name for this ad group.  Example: "Linux".');
        $form['description']['#description'] = t('A description of the ad group.');
        $form['description']['#required'] = TRUE;
        $form['weight']['#description'] = t('In listings, the heavier ad groups will sink and the lighter ad groups will be positioned nearer the top.');
        unset($form['synonyms']);
      }
    }
    else {
      if ($form_id == 'search_form' && variable_get('ad_no_search', 1) && !user_access('administer advertisements') && !user_access('administer any advertisement')) {
        $vid = _ad_get_vid();
        $vocabulary = db_result(db_query('SELECT name FROM {vocabulary} WHERE vid = %d', $vid));
        unset($form['advanced']['category']['#options'][$vocabulary]);
        if (empty($form['advanced']['category']['#options'])) {
          unset($form['advanced']['category']);
        }
        unset($form['advanced']['type']['#options']['ad']);
      }
    }
  }
}

/**
 * Implementation of hook_db_rewrite_sql().
 */
function ad_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
  if (variable_get('ad_no_search', 1) && !user_access('administer advertisements') && !user_access('edit any advertisement') && $query == '' && $primary_table == 'n' && ($primary_field = 'nid' && empty($args))) {
    return array(
      'where' => " n.type != 'ad'",
    );
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function ad_nodeapi(&$node, $op, $teaser, $page) {
  global $user;
  switch ($op) {
    case 'load':
      $ad = db_fetch_array(db_query('SELECT * FROM {ads} WHERE aid = %d', $node->nid));
      $merge = array_merge((array) $node, (array) $ad);
      $adtype = module_invoke('ad_' . $ad['adtype'], 'adapi', 'load', $merge);
      if (is_array($adtype)) {
        return array_merge($ad, $adtype);
      }
      else {
        return $ad;
      }
      break;
    case 'insert':
      if (isset($node->adtype)) {
        if ($node->status != 1 && $node->adstatus == 'active') {
          $node->adstatus = 'expired';
        }
        $activated = $node->adstatus == 'active' ? time() : 0;
        $node->autoactivate = isset($node->autoactivate) ? trim($node->autoactivate) : 0;
        $node->autoactivate = is_numeric($node->autoactivate) ? $node->autoactivate : strtotime($node->autoactivate);
        $node->autoexpire = isset($node->autoexpire) ? trim($node->autoexpire) : 0;
        $node->autoexpire = is_numeric($node->autoexpire) ? $node->autoexpire : strtotime($node->autoexpire);
        if (!isset($node->maxviews)) {
          $node->maxviews = 0;
        }
        if (!isset($node->maxclicks)) {
          $node->maxclicks = 0;
        }
        db_query("INSERT INTO {ads} (aid, uid, adstatus, adtype, redirect, autoactivate, autoexpire, activated, maxviews, maxclicks) VALUES(%d, %d, '%s', '%s', '%s', %d, %d, %d, %d, %d)", $node->nid, $node->uid, $node->adstatus, $node->adtype, url('ad/redirect/' . $node->nid, array(
          'absolute' => TRUE,
        )), $node->autoactivate, $node->autoexpire, $activated, $node->maxviews, $node->maxclicks);
        ad_statistics_increment($node->nid, 'create');
      }
      break;
    case 'update':
      if (isset($node->adtype)) {
        $ad = db_fetch_object(db_query('SELECT * FROM {ads} WHERE aid = %d', $node->nid));
        $node->autoactivate = isset($node->autoactivate) ? trim($node->autoactivate) : 0;
        $node->autoactivate = is_numeric($node->autoactivate) ? $node->autoactivate : strtotime($node->autoactivate);
        $node->autoexpire = isset($node->autoexpire) ? trim($node->autoexpire) : 0;
        $node->autoexpire = is_numeric($node->autoexpire) ? $node->autoexpire : strtotime($node->autoexpire);

        // Ad must be in approved state to be able to autoactivate it.
        if ($node->adstatus != 'approved' && $node->autoactivate) {
          if ($node->adstatus == 'active') {

            // This ad is already active, no need to autoactivate it.
            drupal_set_message(t('This ad is already active, ignoring autoactivate date.'));
            $node->autoactivate = 0;
          }
          else {
            drupal_set_message(t('This ad will not be automatically activated at the scheduled time because it is not in the <em>approved</em> state.'), 'error');
          }
        }

        // If this node has been upublished, the ad should no longer be active.
        if ($node->status != 1 && $node->adstatus == 'active') {
          $node->adstatus = 'expired';
        }

        // Check if ad is being manually activated.
        if ($ad->adstatus != 'active' && $node->adstatus == 'active') {
          $activated = time();
        }
        else {
          if ($ad->adstatus != 'expired' && $node->adstatus == 'expired') {

            // Ad has been manually expired.
            $activated = $ad->activated;
            $expired = time();
          }
          else {
            $activated = $ad->activated;
            $expired = $ad->expired;
          }
        }

        // Ad status has changed, record the event.
        if ($ad->adstatus != $node->adstatus) {
          ad_statistics_increment($node->nid, $node->adstatus);
        }

        // Update ads table with new information.
        db_query("UPDATE {ads} SET uid = %d, adstatus = '%s', adtype = '%s', redirect = '%s', autoactivate = %d, autoexpire = %d, activated = %d, maxviews = %d, maxclicks = %d, expired = %d WHERE aid = %d", $node->uid, $node->adstatus, $node->adtype, url('ad/redirect/' . $node->nid, array(
          'absolute' => TRUE,
        )), $node->autoactivate, $node->autoexpire, isset($activated) ? $activated : 0, isset($node->maxviews) ? (int) $node->maxviews : 0, isset($node->maxclicks) ? (int) $node->maxclicks : 0, isset($expired) ? $expired : 0, $node->nid);
        ad_statistics_increment($node->nid, 'update');
      }
      break;
    case 'delete':

      // All that's left of the ad is a single timestamp as to when it was
      // deleted.
      ad_statistics_increment($node->nid, 'delete');
      db_query("DELETE FROM {ads} WHERE aid = %d", $node->nid);
      db_query("DELETE FROM {ad_statistics} WHERE aid = %d", $node->nid);
      break;
    case 'view':
      if (isset($node->adtype)) {
        if (variable_get('ad_meta_noindex', 0)) {
          ad_noindex_meta();
        }
        $node = node_prepare($node, $teaser);
        $node->content['body'] = array(
          '#value' => $teaser ? $node->teaser : theme('node_ad', $node, $page),
          '#weight' => 1,
        );
      }
      break;
    case 'validate':
      $autoactivate = isset($node->autoactivate) ? trim($node->autoactivate) : '';
      if (!empty($autoactivate)) {
        $timestamp = is_numeric($autoactivate) ? $autoactivate : strtotime($autoactivate);
        if ($timestamp <= 0) {
          form_set_error('autoactivate', 'Please select a valid time to automatically active this ad.');
        }
      }
      $autoexpire = isset($node->autoexpire) ? trim($node->autoexpire) : '';
      if (!empty($autoexpire)) {
        $timestamp = is_numeric($autoexpire) ? $autoexpire : strtotime($autoexpire);
        if ($timestamp <= 0) {
          form_set_error('autoexpire', 'Please select a valid time to automatically expire this ad.');
        }
      }
      break;
  }

  // Allow ad type module to act on nodeapi events.  The adapi hook provides
  // access to additional variables not available in the nodeapi hook.
  if (isset($node->adtype)) {
    $module = "ad_{$node->adtype}";
    module_invoke("ad_{$node->adtype}", 'adapi', $op, $node);
  }

  // Allow ad cache module to act on nodeapi events.
  $cache = variable_get('ad_cache', 'none');
  if ($cache != 'none') {
    $function = "ad_cache_{$cache}" . '_adcacheapi';
    if (function_exists($function)) {
      $function($op, $node);
    }
  }

  // Rebuild the cache after all hooks are invoked.
  switch ($op) {
    case 'insert':
    case 'update':
    case 'delete':
      if (variable_get('ad_cache_file_rebuild_realtime', 0) && isset($node->adtype)) {
        ad_rebuild_cache();
      }
  }
}

/**
 * Add the noindex meta tag.
 */
function ad_noindex_meta() {
  static $added = FALSE;
  if (!$added) {
    drupal_set_html_head('<meta name="robots" content="noindex" />');
    $added = TRUE;
  }
}
function ad_adapi($op, $node = NULL) {
  switch ($op) {
    case 'permissions':
      return array(
        'access statistics' => TRUE,
        'access click history' => TRUE,
        'access activity log' => TRUE,
        'set status as pending' => FALSE,
        'set status as denied' => FALSE,
        'set status from pending to approved' => FALSE,
        'set status from pending to denied' => FALSE,
        'set status from approved to active' => TRUE,
        'set status from approved to offline' => TRUE,
        'set status from active to offline' => TRUE,
        'set status from active to expired' => FALSE,
        'set status from offline to active' => TRUE,
        'set status from offline to expired' => FALSE,
      );
      break;
  }
}

/**
 * Implementation of hook_menu().
 */
function ad_menu() {
  $items = array();
  $items['admin/content/ad'] = array(
    'title' => 'Ads',
    'page callback' => 'ad_admin_list',
    'access arguments' => array(
      'administer advertisements',
    ),
    'description' => 'Configure and manage your advertising system.',
    'file' => 'ad.admin.inc',
  );
  $items['admin/content/ad/list'] = array(
    'title' => 'List',
    'page callback' => 'ad_admin_list',
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'file' => 'ad.admin.inc',
  );
  $items['admin/content/ad/configure'] = array(
    'title' => 'Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ad_admin_configure_settings',
    ),
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 3,
    'file' => 'ad.admin.inc',
  );
  $items['node/add/ad/ahah'] = array(
    'access arguments' => array(
      'create advertisements',
    ),
    'page callback' => 'ad_form_ahah',
    'type' => MENU_CALLBACK,
  );
  ad_menu_add_global_settings($items);
  $items['admin/content/ad/groups'] = array(
    'title' => 'Ad groups',
    'page callback' => 'ad_admin_groups_list',
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 5,
    'file' => 'ad.admin.inc',
  );
  $items['admin/content/ad/groups/list'] = array(
    'title' => 'List',
    'page callback' => 'ad_admin_groups_list',
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
    'file' => 'ad.admin.inc',
  );
  $items['admin/content/ad/groups/add'] = array(
    'title' => 'Create group',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ad_admin_group_form',
    ),
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 3,
    'file' => 'ad.admin.inc',
  );
  $items["admin/content/ad/groups/%ad_group/edit"] = array(
    'title' => 'Edit group',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ad_admin_group_form',
      4,
    ),
    'access arguments' => array(
      'administer advertisements',
    ),
    'weight' => 1,
    'file' => 'ad.admin.inc',
  );
  $items["admin/content/ad/groups/%ad_group/delete"] = array(
    'title' => 'Delete group',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ad_confirm_group_delete',
      4,
    ),
    'access arguments' => array(
      'administer advertisements',
    ),
    'weight' => 2,
    'file' => 'ad.admin.inc',
  );
  $items['admin/content/ad/configure/global'] = array(
    'title' => 'Global settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ad_admin_configure_settings',
    ),
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 0,
    'file' => 'ad.admin.inc',
  );
  $items["node/%node/details/%"] = array(
    'title' => 'Click details',
    'page callback' => 'ad_click_details',
    'page arguments' => array(
      1,
      3,
    ),
    'access arguments' => array(
      1,
      'access click history',
    ),
    'access callback' => 'ad_permission',
    'type' => MENU_CALLBACK,
    'file' => 'ad.pages.inc',
  );
  $items["node/%node/activity"] = array(
    'title' => t('Activity log'),
    'page callback' => 'ad_activity_details',
    'page arguments' => array(
      1,
    ),
    'access arguments' => array(
      1,
      'access activity log',
    ),
    'access callback' => 'ad_permission',
    'type' => MENU_LOCAL_TASK,
    'file' => 'ad.pages.inc',
  );
  $items["ad/redirect/%"] = array(
    'access arguments' => array(
      'show advertisements',
    ),
    'type' => MENU_CALLBACK,
    'page callback' => 'ad_redirect',
    'page arguments' => array(
      2,
    ),
  );
  return $items;
}

/**
 * Load settings for all ad modules. Those modules, who don't
 * have their settings form, will get a standard one.
 */
function ad_menu_add_global_settings(&$menu_items) {
  $adtypes = ad_get_types();
  foreach ($adtypes as $type => $name) {

    // Ad type global settings.
    $settings = 'ad_' . $type . '_global_settings';
    $file = 'ad_image.module';
    if (!function_exists($settings)) {
      $settings = 'ad_no_global_settings';
      $file = 'ad.admin.inc';
    }
    $menu_items['admin/content/ad/configure/' . $type] = array(
      'title' => $name,
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        $settings,
      ),
      'access arguments' => array(
        'administer advertisements',
      ),
      'type' => MENU_LOCAL_TASK,
      'weight' => 2,
      'file' => 'ad.admin.inc',
    );
  }
}

/**
 * Drupal menu wildcard Ad group loader
 */
function ad_group_load($tid) {
  if (!is_numeric($tid)) {
    return FALSE;
  }
  $group = ad_groups_list(TRUE, $tid);
  if (!isset($group)) {
    return FALSE;
  }
  return $group;
}

/**
 * Implementation of hook_block().
 */
function ad_block($op = 'list', $delta = 0, $edit = array()) {
  switch ($op) {
    case 'list':
      $blocks = array();
      $groups = ad_groups_list();
      foreach ($groups as $tid => $name) {
        $blocks[$tid] = array(
          'info' => t('ad group: @name', array(
            '@name' => $name,
          )),
          'cache' => BLOCK_CACHE_PER_PAGE,
        );
      }
      return $blocks;
    case 'configure':
      $form['ad_block_quantity_' . $delta] = array(
        '#type' => 'select',
        '#title' => t('Number of ads'),
        '#default_value' => variable_get('ad_block_quantity_' . $delta, 1),
        '#options' => drupal_map_assoc(array(
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10,
          11,
          12,
          13,
          14,
          15,
          16,
          17,
          18,
          19,
          20,
          21,
          22,
          23,
          24,
          25,
        )),
        '#description' => t('Select the maximum number of unique ads that should be displayed together in this block.  If you specify a number larger than the maximum number of ads in this ad group, all ads will be displayed once.'),
      );
      return $form;
    case 'save':
      variable_set('ad_block_quantity_' . $delta, $edit['ad_block_quantity_' . $delta]);
      break;
    case 'view':
      $block['content'] = ad($delta, variable_get('ad_block_quantity_' . $delta, 1));
      return $block;
  }
}

/**
 * Determine whether the user has a given privilege.
 *
 * @param $aid
 *   ID of advertisement.
 * @param $permission
 *   Permission string which should be checked (such as 'access click history')
 * @param $account
 *   User object, which are accessing the ad or current user by default.
 */
function ad_permission($aid, $string, $account = NULL) {
  global $user;
  $access = FALSE;

  // See bug http://drupal.org/node/664956
  // Using substr() as AdBard has a custom ad_adbard type.
  if (is_object($aid) && substr($aid->type, 0, 2) != 'ad') {
    return FALSE;
  }

  // by default, check permission for current user
  if (!isset($account)) {
    $account = $user;
  }

  // user #1 has all privileges
  if ($account->uid == 1) {
    return TRUE;
  }

  // if you have administer permissions, you have all permissions
  if (user_access('administer advertisements', $account)) {
    return TRUE;
  }

  // when used in the Drupal menu, $aid may be the full ad object.
  if (is_object($aid) && isset($aid->aid)) {
    $aid = $aid->aid;
  }
  else {
    if (is_object($aid) && isset($aid->nid)) {
      $aid = $aid->nid;
    }
    else {
      if (is_object($aid)) {
        watchdog('ad', 'Invalid aid object passed into ad_permission, no aid->aid set.');
        $aid = 0;
      }
    }
  }

  // invoke ad_owners module to determine user's access
  if (module_exists('ad_owners') && function_exists('ad_owners_permission')) {
    $access = ad_owners_permission($aid, $string, $account);
  }
  else {
    if (in_array($string, array(
      'access statistics',
      'access click history',
    ))) {
      $access = TRUE;
    }
  }

  // with no ad_owners module, all other permissions are denied unless user
  // has 'administer advertisements' permission
  return $access;
}

/**
 * Returns ad types data.
 *
 * @param $op
 *  If set to 'name', will only return array of type names ($type => $name).
 *  If set to 'data', will return all of the type's data
 * @param $type
 *  If not specified, will return array of all available types, else will
 *  return specific type's name or data.
 */
function ad_get_types($op = 'name', $type = NULL) {
  $adtypes = module_invoke_all('adapi', 'type', array());
  switch ($op) {
    case 'name':
      if (isset($type)) {
        return $adtypes[$type]['name'];
      }
      else {
        foreach ($adtypes as $type => $data) {
          $adtypes[$type] = $data['name'];
        }
        return $adtypes;
      }
    case 'data':
      if (isset($type)) {
        return $adtypes[$type];
      }
      else {
        return $adtypes;
      }
  }
}

/**
 * Return an array of all groups, or a specific group.
 *
 * @param $object
 *  If FALSE, will return only name of group(s).  If TRUE, will return full
 *  group object including ->name, ->description, and ->tid.
 * @param $tid
 *  If set to an integer >0, will only return group info about that specific
 *  group.
 */
function ad_groups_list($object = FALSE, $tid = NULL) {
  static $groups = array();
  static $names = array();

  // Return the full group object(s).
  if ($object) {
    if (empty($groups)) {
      $tids = taxonomy_get_tree(_ad_get_vid());
      if (is_array($tids)) {
        foreach ($tids as $group) {
          $groups[$group->tid]->name = $group->name;
          $groups[$group->tid]->description = $group->description;
          $groups[$group->tid]->tid = $group->tid;
          $groups[$group->tid]->weight = $group->weight;
        }
      }

      // Hard coded "default" group with tid of 0.
      $groups[0]->name = t('default');
      $groups[0]->description = t('The default ad group is comprised of all ads not assigned to any other ad group.');
      $groups[0]->tid = 0;
      $groups[0]->weight = 0;
    }

    // Return a specific group object.
    if ((int) $tid) {
      return $groups[$tid];
    }
    else {
      return $groups;
    }
  }
  else {
    if (empty($names)) {
      $tids = taxonomy_get_tree(_ad_get_vid());
      if (is_array($tids)) {
        foreach ($tids as $group) {
          $names[$group->tid] = $group->name;
        }
      }

      // Hard coded "default" group with tid of 0.
      $names[0] = t('default');
    }

    // Return a specific group name.
    if ((int) $tid) {
      return $names[$tid];
    }
    else {
      return $names;
    }
  }
}
function _ad_check_installation() {

  // Verify serve.php exists and is readable.
  $adserve = variable_get('adserve', '');
  $adserveinc = variable_get('adserveinc', '');
  if (!file_exists($adserve)) {

    // The serve.php file should be in the same directory as the ad.module.
    $adserve = drupal_get_path('module', 'ad') . '/serve.php';
    variable_set('adserve', $adserve);
  }
  if (!is_readable($adserve)) {
    variable_set('adserve', '');
    drupal_set_message(t('Failed to read the required file %filename.  Please make the file readable by the webserver process.  No ads can be displayed until this problem is resolved.', array(
      '%filename' => $adserve,
    )), 'error');
  }
  if (!file_exists($adserveinc)) {

    // The adserve.inc file should be in the same directory as the ad.module.
    $adserveinc = drupal_get_path('module', 'ad') . '/adserve.inc';
    variable_set('adserveinc', $adserveinc);
  }
  if (!is_readable($adserveinc)) {
    variable_set('adserveinc', '');
    drupal_set_message(t('Failed to read the required file %filename.  Please make the file readable by the webserver process.  No ads can be displayed until this problem is resolved.', array(
      '%filename' => $adserveinc,
    )), 'error');
  }

  // Validate vid in vocabulary table.
  $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = 'ad'"));
  if ($vid != variable_get('ad_group_vid', '')) {
    drupal_set_message(t('Invalid vocabulary defined for advertisements, attempting to auto-fix.'), 'error');
    if ($vid) {
      db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d OR type = 'ad'", variable_get('ad_group_vid', ''));
      variable_set('ad_group_vid_restore', variable_get('ad_group_vid', ''));
    }
    variable_del('ad_group_vid');
  }
  else {

    // Validate vid in vocabulary_node_types table.
    $result = db_query("SELECT vid FROM {vocabulary_node_types} WHERE type = 'ad'");
    $found = FALSE;
    while ($vocab = db_fetch_object($result)) {
      if ($vocab->vid == variable_get('ad_group_vid', '')) {
        $found = TRUE;
      }
    }
    if (!$found) {
      drupal_set_message(t('Missing vocabulary node type for advertisements, attempting to auto-fix.'), 'error');
      db_query("DELETE FROM {vocabulary_node_types} WHERE vid = %d OR type = 'ad'", variable_get('ad_group_vid', ''));
      db_query("DELETE FROM {vocabulary} WHERE vid = %d", variable_get('ad_group_vid', ''));
      variable_set('ad_group_vid_restore', variable_get('ad_group_vid', ''));
      variable_del('ad_group_vid');
    }
  }
  _ad_get_vid();

  // Preserve old ad groups, if any.
  if (($old = variable_get('ad_group_vid_restore', '')) && ($vid = variable_get('ad_group_vid', ''))) {
    drupal_set_message(t('Restoring orphaned ad group configuration.'));
    db_query('UPDATE {term_data} SET vid = %d WHERE vid = %d', $vid, $old);
    variable_set('ad_group_vid_restore', '');
  }
  $rid = db_result(db_query_range("SELECT rid FROM {permission} WHERE perm LIKE '%%show advertisements%%'", 1));
  if (!$rid) {
    drupal_set_message(t('Be sure to enable "!show" permissions for all roles that you wish to see advertisements.', array(
      '!show' => l(t('show advertisements'), 'admin/user/permissions'),
    )));
  }

  // Allow modules to define an action to take each time an ad is served.
  // When modules define 'adserve_select' or 'adserve_filter', they must set
  // the 'function' and 'path' parameters.  The 'weight' parameter can
  // optionally be set.
  //  function: the function to call when serving an add
  //  path: the path to the include file where $function is defined
  // Modules can define actions that happen when advertisements are served.
  // Currently support actions are:
  //  - init_text   (display content before displaying ads) // TODO
  //  - select      (alter which ads are selected to be displayed) // TODO
  //  - filter      (filter selected ads before they are displayed) // TODO
  //  - exit_text   (display content after displaying ads) // TODO
  $hooks = array(
    'init_text',
    'select',
    'filter',
    'exit_text',
  );
  foreach ($hooks as $hook) {
    $adserve_actions = module_invoke_all('adapi', "adserve_{$hook}", array());
    $actions = array();
    foreach ($adserve_actions as $name => $action) {
      if (is_numeric($action['weight'])) {
        $weight = $action['weight'];
      }
      else {

        // weight is an optional, defaults to 0
        $weight = $action['weight'] = 0;
      }
      $actions[$weight . '.' . $name] = $action;
      $actions[$weight . '.' . $name]['name'] = $name;
    }

    // order actions by weight (multiple same-weight actions sorted by alpha)
    ksort($actions);
    variable_set("adserve_{$hook}", serialize($actions));
  }
  module_invoke_all('adapi', 'check_install', array());
}

/**
 * Creates a vocabulary for use by ad groups if not already created.
 */
function _ad_get_vid() {
  $vid = variable_get('ad_group_vid', '');
  if (empty($vid)) {

    // No vid stored in the variables table, check if one even exists.
    $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = '%s'", 'ad'));
    if (!$vid) {

      // No vid, so we create one.
      $edit = array(
        'name' => t('Ad groups'),
        'multiple' => 1,
        'required' => 0,
        'hierarchy' => 0,
        'relations' => 0,
        'module' => 'ad',
        'nodes' => array(
          'ad' => 1,
        ),
      );
      taxonomy_save_vocabulary($edit);
      $vid = $edit['vid'];
    }

    // Save the vid for next time.
    variable_set('ad_group_vid', $vid);
  }
  return $vid;
}

/**
 * Builds the necessary HTML to display an image-based impressions counter.
 */
function ad_display_image($aid, $css = TRUE) {
  global $base_url;
  $adserve = variable_get('adserve', '');
  $cache = variable_get('ad_cache', 'none');
  $variables = "?o=image";
  $variables .= "&a={$aid}";
  if ($cache != 'none') {
    $variables .= '&c=' . $cache . module_invoke('ad_cache_' . $cache, 'adcacheapi', 'display_variables', array());
  }
  $output = '<img src="' . htmlentities(url($base_url . '/' . $adserve . $variables)) . '" height="0" width="0" alt="view counter" />';
  if ($css) {
    return '<div class="ad-image-counter">' . $output . '</div>';
  }
  else {
    return $output;
  }
}

/**
 * Retrieve the group name from the nid.
 */
function _ad_get_group($nid) {
  static $groups = array();
  if (!isset($groups[$nid])) {
    $result = db_query('SELECT td.name FROM {term_data} td INNER JOIN {term_node} tn ON td.tid = tn.tid INNER JOIN {node} n ON tn.vid = n.vid WHERE tn.nid = %d AND td.vid = %d', $nid, _ad_get_vid());
    while ($term = db_fetch_object($result)) {
      $terms[] = $term->name;
    }
    if (!empty($terms)) {
      $groups[$nid] = implode(', ', $terms);
    }
    else {
      $groups[$nid] = t('default');
    }
  }
  return $groups[$nid];
}

/**
 * Implements hook_views_api().
 */
function ad_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'ad') . '/views',
  );
}

Functions

Namesort descending Description
ad Use this function to display ads from a specified group.
ad_access Implementation of hook_access().
ad_adapi
ad_block Implementation of hook_block().
ad_cron Implementation of hook_cron().
ad_db_rewrite_sql Implementation of hook_db_rewrite_sql().
ad_display_image Builds the necessary HTML to display an image-based impressions counter.
ad_form Implementation of hook_form().
ad_form_add_adtype_elements Loads Ad type elements into form.
ad_form_ahah Ad type switch AHAH menu handler.
ad_form_alter Implementation of hook_form_alter().
ad_get_types Returns ad types data.
ad_groups_list Return an array of all groups, or a specific group.
ad_group_load Drupal menu wildcard Ad group loader
ad_help Implementation of hook_help().
ad_init Implementation of hook_init.
ad_link_attributes Ad API Helper Function: Append all necessary attributes to <a> tags.
ad_link_nofollow Ad API Helper Function: Append rel="nofollow" if globally enabled.
ad_link_target Ad API Helper Function: Provide XHTML-strict-compatible target window onclick-handlers based on global configuration.
ad_menu Implementation of hook_menu().
ad_menu_add_global_settings Load settings for all ad modules. Those modules, who don't have their settings form, will get a standard one.
ad_nodeapi Implementation of hook_nodeapi().
ad_node_info Implementation of hook_node_info().
ad_noindex_meta Add the noindex meta tag.
ad_perm Implementation of hook_perm().
ad_permission Determine whether the user has a given privilege.
ad_rebuild_cache Force the cache to be flushed.
ad_redirect Update click counter then redirect host to ad's target URL.
ad_select_adtype Ad type switch submit handler.
ad_statistics_increment Increment action counter.
ad_status_array Return an array with all status values user has permission to set. A user with 'administer advertisements' permission can update any status.
ad_theme Implementation of hook_theme().
ad_views_api Implements hook_views_api().
theme_ad_display Function to display the actual advertisement to the screen. Wrap it in a theme function to make it possible to customize in your own theme.
theme_ad_status_display Display the status of the currently viewed ad.
_ad_check_installation
_ad_get_group Retrieve the group name from the nid.
_ad_get_vid Creates a vocabulary for use by ad groups if not already created.