You are here

ad.module in Advertisement 6

An advertising system for Drupal powered websites.

Copyright (c) 2005-2009. Jeremy Andrews <jeremy@tag1consulting.com>.

File

ad.module
View source
<?php

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

/**
 * 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';
      $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;
        }
      }
      $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.
        $append = 'frameborder="' . variable_get('ad_iframe_frameborder', 0) . '" ';
        $append .= 'scrolling="' . variable_get('ad_iframe_scroll', 'auto') . '" ';
        $append .= 'name="' . $group . '" ';
        if ($height = variable_get('ad_iframe_height', '')) {
          $append .= 'height="' . $height . '" ';
        }
        if ($width = variable_get('ad_iframe_width', '')) {
          $append .= 'width="' . $width . '" ';
        }
        $output = '<iframe src="' . $src . "\" {$append}></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="' . $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 = 0;

  // 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') {
    drupal_add_js('misc/jquery.js', 'core');
    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 {
    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, $hostid = 0) {
  global $user;
  if (function_exists('click_filter_status')) {
    $status = click_filter_status($aid, $hostid);
    if ($status == CLICK_VALID) {
      ad_statistics_increment($aid, 'click', $group, $hostid);
    }
  }
  else {

    // We're not filtering clicks, so all clicks are valid.
    ad_statistics_increment($aid, 'click', $group, $hostid);
    $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, hostid, url, timestamp) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', %d)", $aid, $user->uid, $status, ip_address(), $_SERVER['HTTP_USER_AGENT'], $group, $hostid, $url, time());

  // 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)) {
    watchdog('ad', 'Clicked %type ad aid %aid hostid %hostid.', array(
      '%type' => $adtype,
      '%aid' => $aid,
      '%hostid' => $hostid,
    ));
    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;
}

/**
 * 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);
}
function ad_status_array($admin = TRUE) {
  if ($admin) {

    // status options for administrators
    return array(
      'pending' => t('This advertisement is currently waiting for administrative approval.'),
      'approved' => t('This advertisement has been approved and is currently waiting to be administratively activated.'),
      'active' => t('This advertisement is actively being displayed.'),
      'offline' => t('This advertisement has been temporarily disabled by its owner and is not currently being displayed.'),
      'unpublished' => t('This advertisement has been unpublished and is not currently being displayed.'),
      'expired' => t('This advertisement has expired.'),
      'denied' => t('This advertisement was refused by the site administrator, it will not be displayed.'),
    );
  }
  else {

    // status options for advertisement owners
    return array(
      'active' => t('This advertisement is actively being displayed.'),
      'offline' => t('This advertisement has been temporarily disabled and is not currently being displayed.'),
    );
  }
}

/**
 * Display the status of the currently viewed ad.
 */
function theme_ad_status_display($node) {
  $status_array = ad_status_array();
  $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_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',
    '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.'),
  );
  $adtypes = ad_get_types('data');

  // New 'type' notation returns everything we need to define content type
  foreach ($adtypes as $type => $data) {
    $items['ad/' . $type] = $data;
  }
  return $items;
}

/**
 * Implementation of hook_access().
 */
function ad_access($op, $node, $account) {
  if ($op == 'create') {
    return user_access('create advertisements', $account);
  }
  if ($op == 'update' || $op == 'delete') {
    return user_access('administer advertisements', $account) || module_exists('ad_owners') && ad_is_owner($node->nid) && user_access('edit own advertisements', $account);
  }
}

/**
 * Implementation of hook_form().
 */
function ad_form($node, &$form_state) {
  $form = array();
  $type = arg(3);
  if (function_exists('ad_' . $type . '_display_ad')) {
    $adtype = $type;
  }
  else {
    $adtype = isset($node->adtype) ? $node->adtype : '';
  }
  $form['aid'] = array(
    '#type' => 'value',
    '#value' => isset($node->nid) ? $node->nid : 0,
  );
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title'),
    '#required' => TRUE,
    '#default_value' => isset($node->title) ? $node->title : '',
  );
  $form['body_filter']['body'] = array(
    '#type' => 'textarea',
    '#title' => t('Description'),
    '#default_value' => isset($node->body) ? $node->body : '',
    '#rows' => 3,
  );
  $form['body_filter']['format'] = filter_form($node->format);

  // determine the current ad type
  if (!isset($adtype)) {
    $adtypes = ad_get_types();
    if (sizeof($adtypes) == 1) {
      $adtype = key($adtypes);
    }
    else {
      if (!sizeof($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 type-specific options
  if (isset($adtype)) {
    $elements = module_invoke('ad_' . $adtype, 'adapi', 'form', $node);
    if (is_array($elements)) {
      foreach ($elements as $element => $values) {
        $form[$element] = $values;
      }
    }
    $form['adtype'] = array(
      '#type' => 'hidden',
      '#value' => $adtype,
    );
  }
  if (user_access('administer advertisements')) {

    // admins can set any status on advertisements
    $form['adstatus'] = array(
      '#type' => 'fieldset',
      '#title' => t('Status'),
      '#collapsible' => TRUE,
    );
    foreach (ad_status_array() 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',
        ),
      );
    }
  }
  else {
    if (ad_adaccess($node, 'manage status')) {
      if (!$node->adstatus || $node->adstatus == 'pending') {
        $adstatus = ad_status_array();
        $node->adstatus = 'pending';
        $form['adstatus'] = array(
          '#type' => 'fieldset',
          '#title' => t('Status'),
          '#collapsible' => TRUE,
        );
        $form['adstatus']['display'] = array(
          '#type' => 'markup',
          '#value' => '<p><strong>' . t('Status') . ':</strong> ' . t($node->adstatus) . '<br />' . t($adstatus[$node->adstatus]),
        );
        $form['adstatus']['adpending'] = array(
          '#type' => 'value',
          '#value' => $node->adstatus,
        );
      }
      else {
        $adstatus = ad_status_array(FALSE);

        // display status options
        $form['adstatus'] = array(
          '#type' => 'fieldset',
          '#title' => t('Status'),
          '#collapsible' => TRUE,
        );
        foreach ($adstatus as $status => $description) {
          $form['adstatus']["ad{$status}"] = array(
            '#type' => 'radio',
            '#title' => t($status),
            '#return_value' => $status,
            '#default_value' => $node->adstatus ? $node->adstatus : 'pending',
            '#description' => $description,
            '#parents' => array(
              "adstatus",
            ),
          );
        }
      }
    }
    else {
      $adstatus = ad_status_array();
      if (!isset($node->adstatus)) {
        $node->adstatus = 'pending';
      }
      $form['ad_adstatus'] = array(
        '#type' => 'fieldset',
        '#title' => t('Status'),
        '#collapsible' => TRUE,
      );
      $form['ad_adstatus']['adstatus_display'] = array(
        '#type' => 'markup',
        '#value' => '<p><strong>' . t('Status') . ':</strong> ' . t($node->adstatus) . '<br />' . t($adstatus[$node->adstatus]),
      );
      $form['adstatus'] = array(
        '#type' => 'value',
        '#value' => $node->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,
  );
  if (ad_adaccess($node, 'manage status')) {
    $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' => 'textfield',
      '#title' => t('Automatically activate ad'),
      '#required' => FALSE,
      '#default_value' => isset($node->autoactivate) && $node->autoactivate > 0 ? format_date((int) $node->autoactivate, 'custom', 'F j, Y H:i') : 0,
      '#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 (user_access('administer advertisements')) {

    // admins can expire advertisements
    $form['schedule']['autoexpire'] = array(
      '#type' => 'textfield',
      '#title' => t('Automatically expire ad'),
      '#required' => FALSE,
      '#default_value' => isset($node->autoexpire) && $node->autoexpire > 0 ? format_date((int) $node->autoexpire, 'custom', 'F j, Y H:i') : 0,
      '#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.'),
    );
    $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,
    );
  }
  return $form;
}

/**
 * Implementation of hook_form_alter().
 */
function ad_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == 'ad_node_form') {
    $adtypes = ad_get_types('data');
    if (isset($form['adtype']) && isset($form['adtype']['#value'])) {
      $adtype = $form['adtype']['#value'];
      if (isset($adtypes[$adtype])) {

        // Valid advertisement type selected.
        return;
      }
    }
    if (sizeof($adtypes) == 1) {

      // Auto select the appropriate advertisement type.
      drupal_goto('node/add/ad/' . key($adtypes));
    }
    else {
      foreach ($adtypes as $key => $adtype) {
        $out = '<dt>' . l($adtype['name'], "node/add/ad/{$key}", array(
          'title' => t('Add a !key', array(
            '!key' => $adtype['name'],
          )),
        )) . '</dt>';
        $out .= '<dd>' . $adtype['description'] . '</dd>';
        $item[$key] = $out;
      }
      if (isset($item)) {
        $output = t('Choose from the following advertisement types:');
        $output .= '<dl>' . implode('', $item) . '</dl>';
      }
      else {
        $output = t('You are not allowed to create advertisements.');
      }
      $form = array();
      $form['select'] = array(
        '#value' => $output,
      );
      $form['#tree'] = FALSE;
      $form['#programmed'] = FALSE;
    }
  }
  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']);
      }
    }
  }
}

/**
 * Submit handler for global settings of all ad types.
 *
 * @see ad_form_alter()
 */
function ad_global_settings_submit($form, &$form_state) {
  variable_set('ad_' . $form_state['values']['adtype'] . '_default_permissions', $form_state['values']['default_permissions']);
  unset($form_state['values']['adtype'], $form_state['values']['default_permissions']);
}

/**
 * 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 = 'unpublished';
        }
        $activated = $node->adstatus == 'active' ? time() : 0;
        if (!isset($node->autoactive)) {
          $node->autoactivate = 0;
        }
        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 ? strtotime($node->autoactivate) : '', $node->autoexpire ? strtotime($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));

        // 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.
            $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 = 'unpublished';
        }
        else {
          if ($node->status == 1 && $node->adstatus == 'unpublished') {
            $node->adstatus = 'active';

            // Special "publish" event, may as well track it even though we'll
            // next also record an "active" event.
            ad_statistics_increment($node->nid, 'publish');
          }
        }

        // 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.
            $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,
        )), isset($node->autoactivate) && $node->autoactivate > 0 ? strtotime($node->autoactivate) : '', isset($node->autoexpire) && $node->autoexpire > 0 ? strtotime($node->autoexpire) : '', $activated, 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':
      db_query("DELETE FROM {ads} WHERE aid = %d", $node->nid);
      db_query("DELETE FROM {ad_statistics} WHERE aid = %d", $node->nid);

      // All that's left of the ad is a single timestamp as to when it was
      // deleted.
      ad_statistics_increment($node->nid, 'delete');
      break;
    case 'view':
      if (isset($node->adtype)) {
        $node = node_prepare($node, $teaser);
        $node->content['body'] = array(
          '#value' => $teaser ? $node->teaser : theme('node_ad', $node, $page),
          '#weight' => 1,
        );
      }
      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)) {

    // Don't use module_invoke, as in pre-PHP5 the changes to $node won't be
    // passed back.
    $function = "ad_{$node->adtype}" . '_adapi';
    if (function_exists($function)) {
      $function($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);
    }
  }
}
function ad_adapi($op, $node = NULL) {
  switch ($op) {
    case 'permissions':
      return array(
        'access statistics',
        'access click history',
        'manage status',
      );
      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/statistics'] = array(
    'title' => 'Statistics',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'ad_admin_statistics',
    ),
    'access arguments' => array(
      'administer advertisements',
    ),
    'type' => MENU_LOCAL_TASK,
    'weight' => 1,
    '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',
  );
  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_adaccess',
    'type' => MENU_CALLBACK,
    'file' => 'ad.pages.inc',
  );
  $items["ad/redirect/%/%/%"] = array(
    'access arguments' => array(
      'show advertisements',
    ),
    'type' => MENU_CALLBACK,
    'page callback' => 'ad_redirect',
    'page arguments' => array(
      2,
      3,
      4,
    ),
  );
  $items["ad/redirect/%/%"] = array(
    'access arguments' => array(
      'show advertisements',
    ),
    'type' => MENU_CALLBACK,
    'page callback' => 'ad_redirect',
    'page arguments' => array(
      2,
      3,
    ),
  );
  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';
    if (!function_exists($settings)) {
      $settings = 'ad_no_global_settings';
    }
    $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]['info'] = t('ad group: @name', array(
          '@name' => $name,
        ));
      }
      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':
      $groups = ad_groups_list();
      $block['content'] = ad($delta, variable_get('ad_block_quantity_' . $delta, 1));
      return $block;
  }
}

/**
 * Determine whether the user has a given privilege.
 *
 * @param $ad
 *   Node object or aid of advertisement.
 * @param $permission
 *   Special Ad owners permission which should be checked (such as 'manage owners')
 * @param $account
 *   User object, which are accessing the ad or current user by default.
 */
function ad_adaccess($ad, $permission, $account = NULL) {
  global $user;
  static $permissions = array();
  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;
  }

  // Handle ad owners access
  if (module_exists('ad_owners')) {
    return ad_owners_adaccess($ad, $permission, $account);
  }
  return FALSE;
}

/**
 * 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;
    }
  }
}

/**
 * Implement ad notify api _hook.
 */
function ad_adnotifyapi($op, $arg1 = NULL, $arg2 = NULL) {
  switch ($op) {

    // Make the following events available for notification.
    case 'register':
      return array(
        '-expired' => t('Email @when before the advertisement will expire.'),
        'expired' => t('Email @when after the advertisement is expired.'),
        '-active' => t('Email @when before the advertisement will be activated (if scheduled).'),
        'active' => t('Email @when after the advertisement is activated.'),
        'click' => t('Email @when after the advertisement is clicked.'),
        'approved' => t('Email @when after the advertisement is approved.'),
        'denied' => t('Email @when after the advertisement is denied.'),
      );
      break;
    case '-expired':
      $node = node_load($arg1->aid);
      if (isset($node->autoexpire) && $node->autoexpire) {
        if (time() + $arg1->delay >= $node->autoexpire && $arg1->sent + $arg1->delay < $node->autoexpire) {
          return array(
            '-expired' => 1,
          );
        }
      }
      break;
    case '-active':
      $node = node_load($arg1->aid);
      if (isset($node->autoactivate) && $node->autoactivate) {
        if (time() + $arg1->delay >= $node->autoactivate && $arg1->sent + $arg1->delay < $node->autoactivate) {
          return array(
            '-active' => 1,
          );
        }
      }
      break;
    case 'mail_text':
      switch ($arg1) {
        case 'expired':
          return array(
            'subject' => t('[%sitename ad] %event notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" that was being displayed on the %sitename website has expired.\n\n  Your advertisement was viewed %global_views times and clicked %global_clicks times since it was activated on %activated_large.\n\n  You can view additional statistics about this advertisement or update this notification at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
        case '-expired':
          return array(
            'subject' => t('[%sitename ad] expiration notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" that is being displayed on the %sitename website will expire on %autoexpire_large.\n\n  Your advertisement has been viewed %today_views times and clicked %today_clicks times today.  It was viewed %yesterday_views times and clicked %yesterday_clicks times yesterday.  It has been viewed %global_views times and clicked %global_clicks times since it was activated on %activated_large.\n\n  You can view additional statistics about this advertisement or update this notification at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
        case 'active':
          return array(
            'subject' => t('[%sitename ad] %event notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" is now actively being displayed on the %sitename website.\n\n  Your advertisement has been viewed %global_views times and clicked %global_clicks times since it was activated on %activated_large.\n\n  You can view additional statistics about this advertisement or update this notification at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
        case '-active':
          return array(
            'subject' => t('[%sitename ad] activation notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" will be actively displayed on the %sitename website on %autoactivate_large.\n\n  You can view statistics about this advertisement or update this notification at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
        case 'click':
          return array(
            'subject' => t('[%sitename ad] %event notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" on the %sitename website has been clicked.\n\n  Your advertisement has been viewed %today_views times and clicked %today_clicks times today.  It was viewed %yesterday_views times and clicked %yesterday_clicks times yesterday.  It has been viewed %global_views times and clicked %global_clicks times since it was activated on %activated_large.\n\n  You will receive this %frequency  You can view additional statistics about this advertisement or update this notification at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
        case 'approved':
          return array(
            'subject' => t('[%sitename ad] %event notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" on the %sitename website has been approved.\n\n  You can view statistics about this advertisement at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
        case 'denied':
          return array(
            'subject' => t('[%sitename ad] %event notification'),
            'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification to inform you that your advertisement \"%title\" on the %sitename website has been denied and will not be displayed.\n\n  You can view statistics about this advertisement at the following url:\n    %url\n\nRegards,\n The %sitename Team\n\n-\n%siteurl"),
          );
      }
      break;
  }
}
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($ad, $css = TRUE) {
  global $base_url;
  $adserve = variable_get('adserve', '');
  $cache = variable_get('ad_cache', 'none');
  $variables = "?o=image";
  if (is_object($ad)) {
    $aid = $ad->aid;
  }
  else {

    /**
     * No ad is specified, so we're just tracking traffic.
     */
    $aid = 0;
  }
  $variables .= "&amp;a={$aid}";
  if ($cache != 'none') {
    $variables .= '&amp;c=' . $cache . module_invoke('ad_cache_' . $cache, 'adcacheapi', 'display_variables', array());
  }
  $output = '<img src="' . 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 d.name FROM {term_data} d LEFT JOIN {term_node} n ON d.tid = n.tid WHERE n.nid = %d AND d.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];
}

Functions

Namesort descending Description
ad Use this function to display ads from a specified group.
ad_access Implementation of hook_access().
ad_adaccess Determine whether the user has a given privilege.
ad_adapi
ad_adnotifyapi Implement ad notify api _hook.
ad_block Implementation of hook_block().
ad_cron Implementation of hook_cron().
ad_display_image Builds the necessary HTML to display an image-based impressions counter.
ad_form Implementation of hook_form().
ad_form_alter Implementation of hook_form_alter().
ad_get_types Returns ad types data.
ad_global_settings_submit Submit handler for global settings of all ad types.
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_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_perm Implementation of hook_perm().
ad_redirect Update click counter then redirect host to ad's target URL.
ad_statistics_increment Increment action counter.
ad_status_array
ad_theme Implementation of hook_theme().
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.