diff ad.module @ 1:948362c2a207 ad

update advertisement
author pierre
date Thu, 02 Apr 2009 15:28:21 +0000
parents d8a3998dac8e
children e5584a19768b
line wrap: on
line diff
--- a/ad.module	Fri Feb 20 14:04:09 2009 +0000
+++ b/ad.module	Thu Apr 02 15:28:21 2009 +0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: ad.module,v 1.2.2.29.2.83.2.16 2009/02/17 18:56:26 jeremy Exp $
+// $Id: ad.module,v 1.2.2.29.2.83.2.16.2.12 2009/03/31 04:41:03 jeremy Exp $
 
 /**
  * @file
@@ -9,6 +9,7 @@
  *   Jeremy Andrews <jeremy@tag1consulting.com>.
  */
 
+require_once('ad_token.inc');
 
 /**
  * Implementation of hook_theme().
@@ -103,6 +104,7 @@
   switch ($options['ad_display']) {
     case 'raw':
       require_once(drupal_get_path('module', 'ad') .'/adserve.inc');
+      require_once(drupal_get_path('module', 'ad') .'/adcache.inc');
       $output = adserve_ad($options);
       break;
     case 'iframe':
@@ -149,6 +151,12 @@
         $query['t'] = $group;
         $options['tids'] = $group;
       }
+      if (isset($options['url'])) {
+        $query['u'] = $options['url'];
+      }
+      else {
+        $query['u'] = $_GET['q'];
+      }
       $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
@@ -171,13 +179,18 @@
         $output = $src;
       }
       else {
-        $output = '<script type="text/javascript" src="'. $src .'"></script>';
+        $output = "<script type='text/javascript' src='$src'></script>";
       }
       break;
   }
 
   if (user_access('show advertisements')) {
-    return theme('ad_display', $group, $output, $options['ad_display']);
+    if (isset($options['div']) && $options['div'] !== FALSE) {
+      return theme('ad_display', $group, $output, $options['ad_display']);
+    }
+    else {
+      return theme('ad_display', $group, $output, 'raw');
+    }
   }
   else {
     return theme('ad_display', 'none', "<!-- Enable 'show advertisements' permission if you wish to display ads here. -->");
@@ -189,14 +202,20 @@
  * theme function to make it possible to customize in your own theme.
  */
 function theme_ad_display($group, $display, $method = 'javascript') {
-  static $id = 0;
+  static $id = -1;
+
+  // Increment counter for displaying multiple advertisements on the page.
+  $id++;
 
   // The naming convention for the id attribute doesn't allow commas.
   $group = preg_replace('/[,]/', '+', $group);
 
   if ($method == 'jquery') {
     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";
+    return "\n<div class=\"advertisement group-$group\" id=\"group-id-$id\">\n <script type=\"text/javascript\">\n//<![CDATA[\n  $(document).ready(function(){ jQuery(\"div#group-id-$id\").load(\"$display\"); });\n //]]>\n </script>\n</div>\n";
+  }
+  else if ($method == 'raw') {
+    return $display;
   }
   else {
     return "\n<div class=\"advertisement group-$group\" id=\"group-id-$group\">$display</div>\n";
@@ -206,8 +225,10 @@
 /**
  * Update click counter then redirect host to ad's target URL.
  */
-function ad_redirect($aid, $group, $hostid = 0) {
+function ad_redirect($aid, $group = NULL) {
   global $user;
+  $hostid = isset($_GET['hostid']) ? $_GET['hostid'] : '';
+  $extra = isset($_GET['extra']) ? $_GET['extra'] : '';
   if (function_exists('click_filter_status')) {
     $status = click_filter_status($aid, $hostid);
     if ($status == CLICK_VALID) {
@@ -224,7 +245,7 @@
   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());
+  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());
 
   // Determine where we're supposed to redirect the user.
   $adtype = db_result(db_query('SELECT adtype FROM {ads} WHERE aid = %d', $aid));
@@ -274,6 +295,20 @@
 }
 
 /**
+ * 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();
+  }
+}
+
+/**
  * Ad API Helper Function:
  * Append rel="nofollow" if globally enabled.
  */
@@ -307,65 +342,104 @@
   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.'));
+/**
+ * Return an array with all status values user has permission to set.
+ * A user with 'administer advertisements' permission can update any status.
+ */
+function ad_status_array($aid = 0, $status = NULL) {
+  $permissions = array();
+  // mark status as pending
+  if (user_access('administer advertisements') ||
+      $status == 'pending' || $status == NULL ||
+      ad_permission($aid, 'set status as pending')) {
+    $permissions['pending'] = t('This advertisement is currently waiting for administrative approval.');
   }
-  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.'));
+  // mark status from pending to approved
+  if (user_access('administer advertisements') ||
+      $status == 'approved' ||
+      ($status == 'pending' &&
+       ad_permission($aid, 'set status from pending to approved'))) {
+    $permissions['approved'] = t('This advertisement has been approved and is currently waiting to be activated.');
   }
+  // mark status as active (from pending, approved, or offline)
+  if (user_access('administer advertisements') ||
+      $status == 'active' ||
+      ($status == 'approved' &&
+       ad_permission($aid, 'set status from approved to active')) ||
+      ($status == 'offline' &&
+       ad_permission($aid, 'set status from offline to active'))) {
+    $permissions['active'] = t('This advertisement is actively being displayed.');
+  }
+  // mark status as offline (from pending, approved, or active)
+  if (user_access('administer advertisements') ||
+      $status == 'offline' ||
+      ($status == 'approved' &&
+       ad_permission($aid, 'set status from approved to offline')) ||
+      ($status == 'active' &&
+       ad_permission($aid, 'set status from active to offline'))) {
+    $permissions['offline'] = t('This advertisement has been temporarily disabled by its owner and is not currently being displayed.');
+  }
+  // mark status as expired (from active or offline)
+  if (user_access('administer advertisements') ||
+      $status == 'expired' ||
+      ($status == 'active' &&
+       ad_permission($aid, 'set status from active to expired')) ||
+      ($status == 'offline' &&
+       ad_permission($aid, 'set status from offline to expired'))) {
+    $permissions['expired'] = t('This advertisement has expired and is no longer being displayed.');
+  }
+  // mark status as denied (from pending or any)
+  if (user_access('administer advertisements') ||
+      $status == 'denied' ||
+      ($status == 'pending' &&
+       ad_permission($aid, 'set status from pending to denied')) ||
+      ad_permission($aid, 'set status as denied')) {
+    $permissions['denied'] = t('This advertisement was refused by the site administrator and will not be displayed.');
+  }
+  return $permissions;
 }
 
 /**
  * Display the status of the currently viewed ad.
  */
 function theme_ad_status_display($node) {
-  $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;
+  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);
   }
-  $output .= '</div>';
-  return theme('box', t('Status'), $output);
 }
 
 /**
@@ -457,6 +531,9 @@
   return array('administer advertisements',
                 'create advertisements',
                 'edit own advertisements',
+                'edit any advertisement',
+                'delete own advertisements',
+                'delete any advertisement',
                 'show advertisements');
 }
 
@@ -469,12 +546,6 @@
     '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;
 }
 
@@ -482,28 +553,28 @@
  * 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)));
+  switch ($op) {
+    case 'create':
+      return (user_access('create advertisements', $account) || user_access('administer advertisements'));
+    case 'update':
+      return (user_access('edit any advertisement', $account) || (user_access('edit own advertisements', $account) && is_ad_owner($node->nid)) || user_access('administer advertisements', $account));
+    case 'delete':
+      return (user_access('delete any advertisement', $account) || (user_access('delete own advertisements', $account) && is_ad_owner($node->nid)) || user_access('administer advertisements', $account));
+    case 'view':
+      return (user_access('show advertisements', $account) || user_access('administer advertisements', $account));
   }
 }
 
 /**
  * Implementation of hook_form().
  */
-function ad_form($node, &$form_state) {
+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 : '';
+  // 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(
@@ -536,93 +607,68 @@
     }
   }
 
+  // display ad type switch
+  if (!isset($node->adtype) || isset($node->adtype_select)) {
+    $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
-  if (isset($adtype)) {
-    $elements = module_invoke('ad_'. $adtype, 'adapi', 'form', $node);
-    if (is_array($elements)) {
-      foreach ($elements as $element => $values) {
-        $form[$element] = $values;
-      }
+  if (isset($node->adtype) && $node->adtype) {
+    if (isset($node->adtype_select) && $node->adtype_select && ($node->adtype_select != $node->adtype)) {
+      $node->adtype = $node->adtype_select;
     }
-    $form['adtype'] = array(
-      '#type' => 'hidden',
-      '#value' => $adtype,
+    ad_form_add_adtype_elements($form, $node->adtype, $node);
+    // add ahah wrapper
+    $form['adtype_elements']['#prefix'] = '<div id="adtype-ahah-wrapper">';
+    $form['adtype_elements']['#suffix'] = '</div>';
+  }
+  if (!isset($form['adtype_elements'])) {
+    $form['adtype_elements'] = array(
+      '#value' => '<div id="adtype-ahah-wrapper"></div>',
+      '#weight' => 3.1,
     );
   }
 
-  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
+  // fieldset for updating ad status
+  $form['adstatus'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Status'),
+    '#collapsible' => TRUE,
+    '#weight' => 4,
+  );
+  $nid = isset($node->nid) ? $node->nid : 0;
+  $adstatus = isset($node->adstatus) ? $node->adstatus : '';
+  // display all available status options
+  foreach (ad_status_array($nid, $adstatus) as $status => $description) {
+    $form['adstatus']["ad$status"] = array(
+      '#type' => 'radio',
+      '#title' => t("$status"),
+      '#return_value' => $status,
+      '#default_value' => isset($node->adstatus) ? $node->adstatus : 'pending',
+      '#description' => "$description",
+      '#parents' => array("adstatus")
     );
   }
 
@@ -644,7 +690,8 @@
       ? FALSE : TRUE,
   );
 
-  if (ad_adaccess($node, 'manage status')) {
+  if ((isset($node->nid) && ad_permission($node->nid, 'manage status')) ||
+      user_access('administer advertisements')) {
     $form['schedule']['current'] = array(
       '#type' => 'markup',
       '#prefix' => '<div>',
@@ -702,48 +749,62 @@
     );
   }
 
+  $form['#validate'][] = 'ad_select_adtype';
   return $form;
 }
 
 /**
+ * Ad type switch submit handler.
+ */
+function ad_select_adtype(&$form, &$form_state) {
+  if (!$form_state['values']['adtype'] && !$form_state['values']['adtype_select']) {
+    form_set_error('adtype_select', t('Please, select an Ad type.'));
+  }
+  if (!isset($form_state['values']['adtype']) || isset($form_state['values']['adtype_select']) && $form_state['values']['adtype'] != $form_state['values']['adtype_select']) {
+    $form_state['values']['adtype'] = $form_state['values']['adtype_select'];
+    $form_state['rebuild'] = TRUE;
+  }
+}
+
+/**
+ * Ad type switch AHAH menu handler.
+ */
+function ad_form_ahah() {
+  $form_state = array('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;
+}
+
+/**
  * 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()) {
@@ -784,16 +845,6 @@
 }
 
 /**
- * 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) {
@@ -816,10 +867,10 @@
     case 'insert':
       if (isset($node->adtype)) {
         if ($node->status != 1 && $node->adstatus == 'active') {
-          $node->adstatus = 'unpublished';
+          $node->adstatus = 'expired';
         }
         $activated = $node->adstatus == 'active' ? time() : 0;
-        if (!isset($node->autoactive)) {
+        if (!isset($node->autoactivate)) {
           $node->autoactivate = 0;
         }
         if (!isset($node->maxviews)) {
@@ -837,7 +888,7 @@
       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 != 'approved' && isset($node->autoactive) && $node->autoactivate) {
           if ($node->adstatus == 'active') {
             // This ad is already active, no need to autoactivate it.
             $node->autoactivate = 0;
@@ -848,15 +899,7 @@
         }
         // If this node has been upublished, the ad should no longer be active.
         if ($node->status != 1 && $node->adstatus == 'active') {
-          $node->adstatus = 'unpublished';
-        }
-        // If a previously unpublished node has been published, reactivate the
-        // the ad.
-        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');
+          $node->adstatus = 'expired';
         }
         // Check if ad is being manually activated.
         if ($ad->adstatus != 'active' && $node->adstatus == 'active') {
@@ -865,6 +908,7 @@
         // Check if ad is being manually expired.
         else if ($ad->adstatus != 'expired' && $node->adstatus == 'expired') {
           // Ad has been manually expired.
+          $activated = $ad->activated;
           $expired = time();
         }
         // Ad has not been manually activated or expired, preserve timestamps.
@@ -877,7 +921,7 @@
           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);
+        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) && strlen($node->autoactivate) > 1 ? strtotime($node->autoactivate) : '', isset($node->autoexpire) && strlen($node->autoexpire) > 1 ? 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');
       }
       break;
@@ -918,12 +962,36 @@
       $function($op, $node);
     }
   }
+
+  // Rebuild the cache after all hooks are invoked.
+  switch ($op) {
+    case 'insert':
+    case 'update':
+    case 'delete':
+      if (variable_get('ad_cache_file_rebuild_realtime', 0) &&
+          isset($node->adtype)) {
+        ad_rebuild_cache();
+      }
+  }
 }
 
 function ad_adapi($op, $node = NULL) {
   switch ($op) {
     case 'permissions':
-      return array('access statistics', 'access click history', 'manage status');
+      return array(
+        'access statistics' => TRUE,
+        'access click history' => TRUE,
+        'set status as pending' => FALSE,
+        'set status as denied' => FALSE,
+        'set status from pending to approved' => FALSE,
+        'set status from pending to denied' => FALSE,
+        'set status from approved to active' => TRUE,
+        'set status from approved to offline' => TRUE,
+        'set status from active to offline' => TRUE,
+        'set status from active to expired' => FALSE,
+        'set status from offline to active' => TRUE,
+        'set status from offline to expired' => FALSE,
+      );
       break;
   }
 }
@@ -948,15 +1016,6 @@
     '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',
@@ -966,6 +1025,11 @@
     'weight' => 3,
     'file' => 'ad.admin.inc',
   );
+  $items['node/add/ad/ahah'] = array(
+    'access arguments' => array('create advertisements'),
+    'page callback' => 'ad_form_ahah',
+    'type' => MENU_CALLBACK,
+  );
 
   ad_menu_add_global_settings($items);
 
@@ -1024,21 +1088,15 @@
     'page callback' => 'ad_click_details',
     'page arguments' => array(1, 3),
     'access arguments' => array(1, 'access click history'),
-    'access callback' => 'ad_adaccess',
+    'access callback' => 'ad_permission',
     'type' => MENU_CALLBACK,
     'file' => 'ad.pages.inc',
   );
-  $items["ad/redirect/%/%/%"] = array(
+  $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)),
+    'page arguments' => (array(2)),
   );
 
   return $items;
@@ -1053,8 +1111,10 @@
   foreach ($adtypes as $type => $name) {
     // Ad type global settings.
     $settings = 'ad_'. $type .'_global_settings';
+    $file = 'ad_image.module';
     if (!function_exists($settings)) {
       $settings = 'ad_no_global_settings';
+      $file = 'ad.admin.inc';
     }
     $menu_items['admin/content/ad/configure/'. $type] = array(
       'title' => $name,
@@ -1116,37 +1176,53 @@
 /**
  * Determine whether the user has a given privilege.
  *
- * @param $ad
- *   Node object or aid of advertisement.
+ * @param $aid
+ *   ID of advertisement.
  * @param $permission
- *   Special Ad owners permission which should be checked (such as 'manage owners')
+ *   Permission string which should be checked (such as 'access click history')
  * @param $account
  *   User object, which are accessing the ad or current user by default.
  */
-function ad_adaccess($ad, $permission, $account = NULL) {
+function ad_permission($aid, $string, $account = NULL) {
   global $user;
-  static $permissions = array();
+  $access = FALSE;
 
+  // by default, check permission for current user
   if (!isset($account)) {
     $account = $user;
   }
 
-  // User #1 has all privileges:
+  // user #1 has all privileges
   if ($account->uid == 1) {
     return TRUE;
   }
 
-  // If you have administer permissions, you have all permissions.
+  // 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);
+  // when used in the Drupal menu, $aid may be the full ad object.
+  if (is_object($aid) && isset($aid->aid)) {
+    $aid = $aid->aid;
+  }
+  else if (is_object($aid)) {
+    watchdog('ad', 'Invalid aid object passed into ad_permission, no aid->aid set.');
+    $aid = 0;
   }
 
-  return FALSE;
+  // invoke ad_owners module to determine user's access
+  if (module_exists('ad_owners') &&
+      function_exists('ad_owners_permission')) {
+    $access = ad_owners_permission($aid, $string, $account);
+  }
+  // no ad_owners module, allow acces to statistics and click history
+  else if (in_array($string, array('access statistics', 'access click history'))) {
+    $access = TRUE;
+  }
+  // with no ad_owners module, all other permissions are denied unless user
+  // has 'administer advertisements' permission
+  return $access;
 }
 
 /**
@@ -1285,38 +1361,38 @@
       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"),
+            'subject' => t('[%site-name 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 %site-name website has expired.\n\n  Your advertisement was viewed %global_impressions 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 %site-name Team\n\n-\n%site-url"),
           );
         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"),
+            'subject' => t('[%site-name 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 %site-name website will expire on %autoexpire_large.\n\n  Your advertisement has been viewed %today_impressions times and clicked %today_clicks times today.  It was viewed %yesterday_impressions times and clicked %yesterday_clicks times yesterday.  It has been viewed %global_impressions 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 %site-name Team\n\n-\n%site-url"),
           );
         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"),
+            'subject' => t('[%site-name 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 %site-name website.\n\n  Your advertisement has been viewed %global_impressions 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 %site-name Team\n\n-\n%site-url"),
           );
         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"),
+            'subject' => t('[%site-name 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 %site-name 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 %site-name Team\n\n-\n%site-url"),
           );
         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"),
+            'subject' => t('[%site-name 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 %site-name website has been clicked.\n\n  Your advertisement has been viewed %today_impressions times and clicked %today_clicks times today.  It was viewed %yesterday_impressions times and clicked %yesterday_clicks times yesterday.  It has been viewed %global_impressions 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 %site-name Team\n\n-\n%site-url"),
           );
         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"),
+            'subject' => t('[%site-name 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 %site-name website has been approved.\n\n  You can view statistics about this advertisement at the following url:\n    %url\n\nRegards,\n The %site-name Team\n\n-\n%site-url"),
           );
         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"),
+            'subject' => t('[%site-name 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 %site-name 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 %site-name Team\n\n-\n%site-url"),
           );
       }
       break;