Skip to content
ad.module 58.4 KiB
Newer Older
/**
 * @file
 * An advertising system for Drupal powered websites.
 *
jeremy's avatar
jeremy committed
 * Copyright (c) 2005-2009.
 *   Jeremy Andrews <jeremy@tag1consulting.com>.
jeremy's avatar
jeremy committed
require_once('ad_token.inc');
jeremy's avatar
jeremy committed
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(
jeremy's avatar
jeremy committed
      'arguments' => array(
        'statistics' => NULL,
      ),
    ),
    'node_ad' => array(
jeremy's avatar
jeremy committed
      'arguments' => array(
        'node' => NULL,
        'yield_form' => TRUE,
      ),
    ),
    'ad_admin_ads' => array(
jeremy's avatar
jeremy committed
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'ad_filters' => array(
jeremy's avatar
jeremy committed
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'ad_filter_form' => array(
jeremy's avatar
jeremy committed
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
jeremy's avatar
jeremy committed

jeremy's avatar
jeremy committed
 * Use this function to display ads from a specified group.
 *
 * @param $group
jeremy's avatar
jeremy committed
 *  The ad group tid to display ads from.
jeremy's avatar
jeremy committed
 * @param $quantity
 *  Optionally specify the number of unique ads to display.
jeremy's avatar
jeremy committed
 * @param $options
 *  Any number of options from this list:  hostid, nids.
jeremy's avatar
jeremy committed
function ad($group = FALSE, $quantity = 1, $options = array()) {
  global $base_url;

  $adserve = variable_get('adserve', '');
jeremy's avatar
jeremy committed
  $adserveinc = variable_get('adserveinc', '');
  if (empty($adserve) || empty($adserveinc)) {
jeremy's avatar
jeremy committed
    // This is probably the first time ad() has been called.
jeremy's avatar
jeremy committed
    $adserve = variable_get('adserve', '');
jeremy's avatar
jeremy committed
    $adserveinc = variable_get('adserveinc', '');
jeremy's avatar
jeremy committed
  }
jeremy's avatar
jeremy committed
  if (!file_exists($adserve) || !file_exists($adserveinc)) {
jeremy's avatar
jeremy committed
    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');
jeremy's avatar
jeremy committed
    return (t('The ad module is <a href="@misconfigured">misconfigured</a>.', array('@misconfigured' => url('admin/content/ad/configure'))));
jeremy's avatar
jeremy committed
  }

  // Be sure a display method has been chosen.
jeremy's avatar
jeremy committed
  if (!isset($options['ad_display'])) {
    $options['ad_display'] = variable_get('ad_display', 'javascript');
jeremy's avatar
jeremy committed
  }
jeremy's avatar
jeremy committed
  $options['quantity'] = isset($quantity) ? $quantity : 1;
jeremy's avatar
jeremy committed
  if (!isset($options['tids'])) {
jeremy's avatar
jeremy committed
    $options['tids'] = $group;
  }
  $options['cache'] = variable_get('ad_cache', 'none');

jeremy's avatar
jeremy committed
  switch ($options['ad_display']) {
jeremy's avatar
jeremy committed
    case 'raw':
      require_once(drupal_get_path('module', 'ad') .'/adserve.inc');
jeremy's avatar
jeremy committed
      require_once(drupal_get_path('module', 'ad') .'/adcache.inc');
jeremy's avatar
jeremy committed
      $output = adserve_ad($options);
jeremy's avatar
jeremy committed
      break;
jeremy's avatar
jeremy committed
    case 'iframe':
jeremy's avatar
jeremy committed
    case 'jquery':
      $query['m'] = $options['ad_display'];
jeremy's avatar
jeremy committed
      // Fall through...
jeremy's avatar
jeremy committed
    case 'javascript':
jeremy's avatar
jeremy committed
    default:
      $query['q'] = $quantity;
jeremy's avatar
jeremy committed
      if (isset($options['hostid'])) {
        $query['k'] = $options['hostid'];
jeremy's avatar
jeremy committed
      }
      // Allow external cache files to define additional display variables.
jeremy's avatar
jeremy committed
      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;
jeremy's avatar
jeremy committed
      }
jeremy's avatar
jeremy committed
      if (isset($options['nids'])) {
jeremy's avatar
jeremy committed
        // Choose ads from the provided list of node Id's.
jeremy's avatar
jeremy committed
        $nids = $options['nids'];
        $query['n'] = $nids;
jeremy's avatar
jeremy committed
        $group = "nids-$nids";
jeremy's avatar
jeremy committed
      }
jeremy's avatar
jeremy committed
      else if (isset($options['tids'])) {
jeremy's avatar
jeremy committed
        // Choose ads from the provided list of taxonomy terms.
jeremy's avatar
jeremy committed
        $tids = $options['tids'];
        $query['t'] = $tids;
jeremy's avatar
jeremy committed
        $group = "tids-$tids";
jeremy's avatar
jeremy committed
      }
jeremy's avatar
jeremy committed
      else {
        // Choose ads from the specified group.
        $query['t'] = $group;
jeremy's avatar
jeremy committed
        $options['tids'] = $group;
jeremy's avatar
jeremy committed
      }
jeremy's avatar
jeremy committed
      if (isset($options['url'])) {
        $query['u'] = $options['url'];
      }
      else {
        $query['u'] = $_GET['q'];
jeremy's avatar
jeremy committed
        if ($alias = drupal_get_path_alias($_GET['q'])) {
          $query['l'] = $alias;
        }
jeremy's avatar
jeremy committed
      $src = url($base_url .'/'. $adserve, array('query' => $query));
jeremy's avatar
jeremy committed
      if ($options['ad_display'] == 'iframe') {
jeremy's avatar
jeremy committed
        // 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') .'" ';
jeremy's avatar
jeremy committed
        if ($height = variable_get('ad_iframe_height', '')) {
jeremy's avatar
jeremy committed
        }
        if ($width = variable_get('ad_iframe_width', '')) {
jeremy's avatar
jeremy committed
        }
jeremy's avatar
jeremy committed
        $output = '<iframe src="'. htmlentities($src) ."\" $append></iframe>";
jeremy's avatar
jeremy committed
      }
jeremy's avatar
jeremy committed
      else if ($options['ad_display'] == 'jquery') {
        // The theme function uses this to generate a CSS id for jQuery to use.
        $output = $src;
      }
jeremy's avatar
jeremy committed
      else {
jeremy's avatar
jeremy committed
        $output = "<script type='text/javascript' src='". htmlentities($src) ."'></script>";
jeremy's avatar
jeremy committed
      }
jeremy's avatar
jeremy committed
      break;
jeremy's avatar
jeremy committed
  }

jeremy's avatar
jeremy committed
  if (user_access('show advertisements')) {
jeremy's avatar
jeremy committed
    return theme('ad_display', $group, $output, $options['ad_display']);
jeremy's avatar
jeremy committed
  }
  else {
    return theme('ad_display', 'none', "<!-- Enable 'show advertisements' permission if you wish to display ads here. -->");
  }
jeremy's avatar
jeremy committed
 * 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.
 */
jeremy's avatar
jeremy committed
function theme_ad_display($group, $display, $method = 'javascript') {
jeremy's avatar
jeremy committed
  static $id = -1;

  // Increment counter for displaying multiple advertisements on the page.
  $id++;
jeremy's avatar
jeremy committed

jeremy's avatar
jeremy committed
  // The naming convention for the id attribute doesn't allow commas.
jeremy's avatar
jeremy committed
  $group = preg_replace('/[,]/', '+', $group);

  if ($method == 'jquery') {
jeremy's avatar
jeremy committed
    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";
jeremy's avatar
jeremy committed
  }
jeremy's avatar
jeremy committed
  else if ($method == 'raw') {
    return $display;
  }
jeremy's avatar
jeremy committed
  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.
 */
jeremy's avatar
jeremy committed
function ad_redirect($aid, $group = NULL) {
jeremy's avatar
jeremy committed
  $hostid = isset($_GET['hostid']) ? $_GET['hostid'] : '';
  $extra = isset($_GET['extra']) ? $_GET['extra'] : '';
jeremy's avatar
jeremy committed
  if (function_exists('click_filter_status')) {
    $status = click_filter_status($aid, $hostid);
  }
  else {
    $status = 0;
  }
jeremy's avatar
jeremy committed
  // Allow source url to be passed in.
jeremy's avatar
jeremy committed
  $url = isset($_GET['u']) ? $_GET['u'] : '';
  if (!isset($url) || !valid_url($url)) {
jeremy's avatar
jeremy committed
    $url = referer_uri();
  }
jeremy's avatar
jeremy committed
  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());
jeremy's avatar
jeremy committed
  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.
jeremy's avatar
jeremy committed
  $adtype = db_result(db_query('SELECT adtype FROM {ads} WHERE aid = %d', $aid));
jeremy's avatar
jeremy committed

jeremy's avatar
jeremy committed
  $node->nid = $node->aid = $aid;
jeremy's avatar
jeremy committed
  $node->hostid = $hostid;
  $url = module_invoke('ad_'. $adtype, 'adapi', 'redirect', $node);
  if (isset($url)) {
    header('Location: '. $url);
  }
  else {
jeremy's avatar
jeremy committed
    watchdog('ad', 'Ad redirection failed for aid %aid hostid %hostid, failed to load destination URL. ', array('%aid' => $aid, '%hostid' => $hostid));
/**
 * Ad API Helper Function:
 * Append all necessary attributes to <a> tags.
 */
function ad_link_attributes() {
jeremy's avatar
jeremy committed
  return array_merge(ad_link_target(TRUE), ad_link_nofollow(TRUE));
 * Provide XHTML-strict-compatible target window onclick-handlers based on
 * global configuration.
  switch (variable_get('ad_link_target', '_self')) {
    case '_blank':
jeremy's avatar
jeremy committed
      $target = array('onclick' => 'window.open(this.href); return false;');
jeremy's avatar
jeremy committed
      $target = array('onclick' => 'window.parent.location = this.href; return false;');
jeremy's avatar
jeremy committed
      $target = array('onclick' => 'window.top.location = this.href; return false;');
jeremy's avatar
jeremy committed
      $target = array();
jeremy's avatar
jeremy committed
/**
 * 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('Rebuilding ad cache.');
    }
    $build();
  }
}

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

/**
 * Increment action counter.
 */
jeremy's avatar
jeremy committed
function ad_statistics_increment($aid, $action, $group = NULL, $hostid = NULL) {
  // Update action statistics.
jeremy's avatar
jeremy committed
  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()) {
jeremy's avatar
jeremy committed
    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()) {
jeremy's avatar
jeremy committed
      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);
jeremy's avatar
jeremy committed

  $event = array('aid' => $aid, 'action' => $action, 'hostid' => $hostid);
  module_invoke_all('adapi', 'statistics_increment', $event);
jeremy's avatar
jeremy committed
/**
 * 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
jeremy's avatar
jeremy committed
  if (user_access('administer advertisements') ||
      $status == 'pending' || $status == NULL ||
jeremy's avatar
jeremy committed
      ad_permission($aid, 'set status as pending')) {
    $permissions['pending'] = t('This advertisement is currently waiting for administrative approval.');
jeremy's avatar
jeremy committed
  // mark status from pending to approved
jeremy's avatar
jeremy committed
  if (user_access('administer advertisements') ||
jeremy's avatar
jeremy committed
      $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)
jeremy's avatar
jeremy committed
  if (user_access('administer advertisements') ||
jeremy's avatar
jeremy committed
      $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)
jeremy's avatar
jeremy committed
  if (user_access('administer advertisements') ||
jeremy's avatar
jeremy committed
      $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)
jeremy's avatar
jeremy committed
  if (user_access('administer advertisements') ||
jeremy's avatar
jeremy committed
      $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)
jeremy's avatar
jeremy committed
  if (user_access('administer advertisements') ||
jeremy's avatar
jeremy committed
      $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;
jeremy's avatar
jeremy committed
/**
 * Display the status of the currently viewed ad.
 */
function theme_ad_status_display($node) {
jeremy's avatar
jeremy committed
  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);
jeremy's avatar
jeremy committed
  }
}

jeremy's avatar
jeremy committed
/**
 * 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().
jeremy's avatar
jeremy committed
function ad_help($path, $arg) {
jeremy's avatar
jeremy committed
  $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;
jeremy's avatar
jeremy committed
    case 'node/add/ad':
      $output = '<p>'. t('Advertisements can be randomly displayed to visitors of your website.') .'</p>';
      break;
jeremy's avatar
jeremy committed
/**
jeremy's avatar
jeremy committed
 */
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');
jeremy's avatar
jeremy committed
    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.
jeremy's avatar
jeremy committed
            db_query("UPDATE {ads} SET adstatus = 'active', autoactivate = 0, autoactivated = %d, activated = %d WHERE aid = %d", time(), time(), $ad->aid);
jeremy's avatar
jeremy committed
            ad_statistics_increment($ad->aid, 'autoactivated');
jeremy's avatar
jeremy committed
            ad_statistics_increment($ad->aid, 'active');
jeremy's avatar
jeremy committed

jeremy's avatar
jeremy committed
            watchdog('ad', 'Automatically activated ad %title with nid %nid.', array('%title' => $node->title, '%nid' => $node->nid));
jeremy's avatar
jeremy committed

            // 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) {
jeremy's avatar
jeremy committed
              watchdog('ad', 'Warning: ad %title with nid %nid in approved state has no autoactivate date set.', array('%title' => $node->title, '%nid' => $node->nid));
jeremy's avatar
jeremy committed
              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.
jeremy's avatar
jeremy committed
            db_query("UPDATE {ads} SET adstatus = 'expired', autoexpire = 0, autoexpired = %d, expired = %d WHERE aid = %d", time(), time(), $ad->aid);
jeremy's avatar
jeremy committed
            ad_statistics_increment($ad->aid, 'autoexpired');
            ad_statistics_increment($ad->aid, 'expired');

jeremy's avatar
jeremy committed
            watchdog('ad', 'Automatically expired ad %title with nid %nid.', array('%title' => $node->title, '%nid' => $node->nid));
jeremy's avatar
jeremy committed

            // 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);
jeremy's avatar
jeremy committed
          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));
jeremy's avatar
jeremy committed
      }
    }
    variable_set('ad_cron_timestamp', time());
  }
}

jeremy's avatar
jeremy committed
  return array('administer advertisements',
jeremy's avatar
jeremy committed
                'edit own advertisements',
jeremy's avatar
jeremy committed
                'edit any advertisement',
                'delete own advertisements',
                'delete any advertisement',
jeremy's avatar
jeremy committed
                'show advertisements');
jeremy's avatar
jeremy committed
    'name' => t('Advertisement'),
Morbus Iff's avatar
Morbus Iff committed
    'module' => 'ad',
    'description' => t('Advertisements can be randomly displayed to visitors of your website.'),
jeremy's avatar
jeremy committed
function ad_access($op, $node, $account) {
jeremy's avatar
jeremy committed
  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':
Jeremy Andrews's avatar
Jeremy Andrews committed
      // 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;
      }
jeremy's avatar
jeremy committed
function ad_form(&$node, &$form_state) {
jeremy's avatar
jeremy committed
  $type = node_get_types('type', $node);
jeremy's avatar
jeremy committed
  // 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',
jeremy's avatar
jeremy committed
    '#value' => isset($node->nid) ? $node->nid : 0,
jeremy's avatar
jeremy committed
  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,
    );
  }
jeremy's avatar
jeremy committed
  if ($type->has_body) {
jeremy's avatar
jeremy committed
    $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' => '',
jeremy's avatar
jeremy committed
    );
jeremy's avatar
jeremy committed
    $form['body_field']['format'] = filter_form($node->format);
jeremy's avatar
jeremy committed
  }

  // determine the current ad type
  if (!isset($adtype)) {
jeremy's avatar
jeremy committed
    if (count($adtypes) == 1) {
jeremy's avatar
jeremy committed
      $adtype = key($adtypes);
    }
jeremy's avatar
jeremy committed
    else if (!count($adtypes)) {
jeremy's avatar
jeremy committed
      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');
jeremy's avatar
jeremy committed
  // display ad type switch
jeremy's avatar
jeremy committed
  if ((!isset($node->adtype) || isset($node->adtype_select)) &&
       count($adtypes) >1) {
jeremy's avatar
jeremy committed
    $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 : '',
    );
    $form['select']['adtype_submit'] = array(
      '#type' => 'submit',
      '#value' => t('Select'),
      '#validate' => array('ad_select_adtype'),
      '#ahah' => array(
        'path' => 'node/add/ad/ahah',
        'wrapper' => 'adtype-ahah-wrapper',
      ),
    );
  }
  // display type-specific options
jeremy's avatar
jeremy committed
  if ((isset($node->adtype) && $node->adtype) || count($adtypes) == 1) {
jeremy's avatar
jeremy committed
    if (isset($node->adtype_select) && $node->adtype_select && ($node->adtype_select != $node->adtype)) {
      $node->adtype = $node->adtype_select;
jeremy's avatar
jeremy committed
    if (count($adtypes) == 1) {
      $node->adtype = $adtype;
    }
jeremy's avatar
jeremy committed
    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,
jeremy's avatar
jeremy committed
  // fieldset for updating ad status
  $form['adstatus'] = array(
    '#type' => 'fieldset',
    '#title' => t('Status'),
jeremy's avatar
jeremy committed
    '#collapsible' => TRUE,
    '#weight' => 4,
jeremy's avatar
jeremy committed
  $nid = isset($node->nid) ? $node->nid : 0;
  $adstatus = isset($node->adstatus) ? $node->adstatus : '';
jeremy's avatar
jeremy committed
  // display all available status options
jeremy's avatar
jeremy committed
  foreach (ad_status_array($nid, $adstatus) as $status => $description) {
jeremy's avatar
jeremy committed
    $form['adstatus']["ad$status"] = array(
      '#type' => 'radio',
      '#title' => t("$status"),
      '#return_value' => $status,
jeremy's avatar
jeremy committed
      '#default_value' => isset($node->adstatus) ? $node->adstatus : 'pending',
jeremy's avatar
jeremy committed
      '#description' => "$description",
      '#parents' => array("adstatus")
jeremy's avatar
jeremy committed
    );
jeremy's avatar
jeremy committed
  // display scheduling options
  $form['schedule'] = array(
jeremy's avatar
jeremy committed
    '#type' => 'fieldset',
    '#title' => t('Scheduling'),
jeremy's avatar
jeremy committed
    '#collapsible' => TRUE,
    // Collapse if there isn't any scheduling data set.
jeremy's avatar
jeremy committed
    '#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,
jeremy's avatar
jeremy committed
  $date_api = FALSE;
  if (module_exists('date_popup')) {
    $date_api = TRUE;
  }
jeremy's avatar
jeremy committed
  if ((isset($node->nid) && ad_permission($node->nid, 'manage status')) ||
      user_access('administer advertisements')) {
jeremy's avatar
jeremy committed
    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')))
      );
    }
jeremy's avatar
jeremy committed
    $form['schedule']['autoactivate'] = array(
jeremy's avatar
jeremy committed
      '#type' => $date_api ? 'date_popup' : 'textfield',
jeremy's avatar
jeremy committed
      '#title' => t('Automatically activate ad'),
      '#required' => FALSE,
jeremy's avatar
jeremy committed
      '#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.'),
jeremy's avatar
jeremy committed
    );
jeremy's avatar
jeremy committed
    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;
    }
jeremy's avatar
jeremy committed
  }

  if (user_access('administer advertisements')) {
    // admins can expire advertisements
    $form['schedule']['autoexpire'] = array(
jeremy's avatar
jeremy committed
      '#type' => $date_api ? 'date_popup' : 'textfield',
jeremy's avatar
jeremy committed
      '#title' => t('Automatically expire ad'),
      '#required' => FALSE,
jeremy's avatar
jeremy committed
      '#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.'),
jeremy's avatar
jeremy committed
    );
jeremy's avatar
jeremy committed
    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;
    }
jeremy's avatar
jeremy committed
    $form['schedule']['maxviews'] = array(
jeremy's avatar
jeremy committed
      '#type' => 'textfield',
Alexander Shvets's avatar
Alexander Shvets committed
      '#title' => t('Maximum impressions'),
jeremy's avatar
jeremy committed
      '#required' => FALSE,
jeremy's avatar
jeremy committed
      '#size' => 10,
      '#maxlength' => 11,
jeremy's avatar
jeremy committed
      '#default_value' => isset($node->maxviews) ? $node->maxviews : 0,
Alexander Shvets's avatar
Alexander Shvets committed
      '#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')),
jeremy's avatar
jeremy committed
    );
    $form['schedule']['maxclicks'] = array(
jeremy's avatar
jeremy committed
      '#type' => 'textfield',
      '#title' => t('Maximum clicks'),
      '#required' => FALSE,
jeremy's avatar
jeremy committed
      '#size' => 10,
      '#maxlength' => 11,
jeremy's avatar
jeremy committed
      '#default_value' => isset($node->maxclicks) ? $node->maxclicks : 0,
jeremy's avatar
jeremy committed
      '#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')),
jeremy's avatar
jeremy committed
    );
  }
  else {
    // display expiration time
jeremy's avatar
jeremy committed
    $form['schedule']['autoexpire_display'] = array(
jeremy's avatar
jeremy committed
      '#type' => 'markup',
jeremy's avatar
jeremy committed
      '#prefix' => '<div>',
      '#suffix' => '</div>',
      '#value' => theme('ad_status_display', $node),
    );
    $form['schedule']['autoexpire'] = array(
jeremy's avatar
jeremy committed
      '#type' => 'hidden',
jeremy's avatar
jeremy committed
      '#value' => isset($node->autoexpire) ? $node->autoexpire : 0,
jeremy's avatar
jeremy committed
    );
jeremy's avatar
jeremy committed
  $form['#validate'][] = 'ad_select_adtype';
jeremy's avatar
jeremy committed
/**
 * 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('storage' => NULL, 'submitted' => FALSE);
  $form_build_id = $_POST['form_build_id'];
  $form = form_get_cache($form_build_id, $form_state);
  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);
  $output = drupal_render($form['adtype_elements']);
  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;
}

jeremy's avatar
jeremy committed
/**
jeremy's avatar
jeremy committed
 */
jeremy's avatar
jeremy committed
function ad_form_alter(&$form, &$form_state, $form_id) {
jeremy's avatar
jeremy committed
  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.');
jeremy's avatar
jeremy committed
      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']);
    }
  }
jeremy's avatar
jeremy committed
  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']);
    }
jeremy's avatar
jeremy committed
    unset($form['advanced']['type']['#options']['ad']);
  }
}

/**
 * Implementation of hook_db_rewrite_sql().
 */
function ad_db_rewrite_sql($query, $primary_table, $primary_field, $args) {
jeremy's avatar
jeremy committed
  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)) {
jeremy's avatar
jeremy committed
    return array('where' => " n.type != 'ad'");
  }
jeremy's avatar
jeremy committed
}

 */
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));
jeremy's avatar
jeremy committed
      $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':
jeremy's avatar
jeremy committed
      if (isset($node->adtype)) {
        if ($node->status != 1 && $node->adstatus == 'active') {
jeremy's avatar
jeremy committed
          $node->adstatus = 'expired';
jeremy's avatar
jeremy committed
        $activated = $node->adstatus == 'active' ? time() : 0;
jeremy's avatar
jeremy committed
        $node->autoactivate = isset($node->autoactivate) ? trim($node->autoactivate) : 0;
        $node->autoexpire = isset($node->autoexpire) ? trim($node->autoexpire) : 0;
jeremy's avatar
jeremy committed
        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');
jeremy's avatar
jeremy committed
      if (isset($node->adtype)) {
jeremy's avatar
jeremy committed
        $ad = db_fetch_object(db_query('SELECT * FROM {ads} WHERE aid = %d', $node->nid));
jeremy's avatar
jeremy committed
        $node->autoactivate = isset($node->autoactivate) ? trim($node->autoactivate) : 0;
        $node->autoexpire = isset($node->autoexpire) ? trim($node->autoexpire) : 0;
jeremy's avatar
jeremy committed
        // Ad must be in approved state to be able to autoactivate it.
jeremy's avatar
jeremy committed
        if ($node->adstatus != 'approved' && $node->autoactivate) {
jeremy's avatar
jeremy committed
          if ($node->adstatus == 'active') {
            // This ad is already active, no need to autoactivate it.
jeremy's avatar
jeremy committed
            drupal_set_message(t('This ad is already active, ignoring autoactivate date.'));
jeremy's avatar
jeremy committed
            $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');
          }
jeremy's avatar
jeremy committed
        }
jeremy's avatar
jeremy committed
        // If this node has been upublished, the ad should no longer be active.
        if ($node->status != 1 && $node->adstatus == 'active') {
jeremy's avatar
jeremy committed
          $node->adstatus = 'expired';
jeremy's avatar
jeremy committed
        // Check if ad is being manually activated.
        if ($ad->adstatus != 'active' && $node->adstatus == 'active') {
          $activated = time();
        }
        // Check if ad is being manually expired.
jeremy's avatar
jeremy committed
        else if ($ad->adstatus != 'expired' && $node->adstatus == 'expired') {
jeremy's avatar
jeremy committed
          // Ad has been manually expired.
jeremy's avatar
jeremy committed
          $activated = $ad->activated;
jeremy's avatar
jeremy committed
          $expired = time();
        }
        // Ad has not been manually activated or expired, preserve timestamps.
        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.
jeremy's avatar
jeremy committed
        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 ? strtotime($node->autoactivate) : '', $node->autoexpire ? strtotime($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');
jeremy's avatar
jeremy committed
      // All that's left of the ad is a single timestamp as to when it was
jeremy's avatar
jeremy committed
      // deleted.
      ad_statistics_increment($node->nid, 'delete');
jeremy's avatar
jeremy committed
      db_query("DELETE FROM {ads} WHERE aid = %d", $node->nid);
      db_query("DELETE FROM {ad_statistics} WHERE aid = %d", $node->nid);