 4 files changed, 316 insertions(+), 249 deletions(-)

diff --git a/ad.module b/ad.module
index 4873e6f..92f9d89 100644
--- a/ad.module
+++ b/ad.module
@@ -58,7 +58,6 @@ function ad_theme() {
     ),
   );
 };
-  /**/
 
 /* TODO You may want to take advantage of new form-specific alter hooks.
    The hook_form_alter(&$form, &$form_state, $form_id) is complemented by hook_form_$form-id_alter().
@@ -97,11 +96,11 @@ function ad($group = FALSE, $quantity = 1, $options = array()) {
     _ad_check_install();
     $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_install();
-    return(t('The ad module is <a href="@misconfigured">misconfigured</a>.', array('@misconfigured' => url('admin/content/ad/configure'))));
+    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.
@@ -109,7 +108,7 @@ function ad($group = FALSE, $quantity = 1, $options = array()) {
     $options['ad_display'] = variable_get('ad_display', 'javascript');
   }
   $options['quantity'] = isset($quantity) ? $quantity : 1;
-  if (!isset($options['tids'])) { 
+  if (!isset($options['tids'])) {
     $options['tids'] = $group;
   }
   $options['cache'] = variable_get('ad_cache', 'none');
@@ -161,7 +160,7 @@ function ad($group = FALSE, $quantity = 1, $options = array()) {
         $query['t'] = $group;
         $options['tids'] = $group;
       }
-      $src = url("$base_url/$adserve", array('query' => $query));
+      $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.
@@ -169,21 +168,21 @@ function ad($group = FALSE, $quantity = 1, $options = array()) {
         // over-rides.
         $append = 'frameborder="'. variable_get('ad_iframe_frameborder', 0) .'" ';
         $append .= 'scrolling="'. variable_get('ad_iframe_scroll', 'auto') .'" ';
-        $append .= "name=\"$group\" ";
+        $append .= 'name="'.$group.'" ';
         if ($height = variable_get('ad_iframe_height', '')) {
-          $append .= "height=\"$height\" ";
+          $append .= 'height="'.$height.'" ';
         }
         if ($width = variable_get('ad_iframe_width', '')) {
-          $append .= "width=\"$width\" ";
+          $append .= 'width="'.$width.'" ';
         }
-        $output = "<iframe src=\"$src\" $append></iframe>";
+        $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>";
+        $output = '<script type="text/javascript" src="'.$src.'"></script>';
       }
       break;
   }
@@ -197,7 +196,7 @@ function ad($group = FALSE, $quantity = 1, $options = array()) {
 }
 
 /**
- * Function to display the actual advertisement to the screen.  Wrap it in a 
+ * 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') {
@@ -294,7 +293,7 @@ function ad_link_nofollow() {
   if (variable_get('ad_link_nofollow', 0)) {
     return ' rel="nofollow"';
   }
-  return; 
+  return;
 }
 
 /**
@@ -321,18 +320,18 @@ 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.'), 
+      '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.'), 
+      'active' => t('This advertisement is actively being displayed.'),
       'offline' => t('This advertisement has been temporarily disabled and is not currently being displayed.'));
   }
 }
@@ -371,7 +370,7 @@ function ad_statistics($aid) {
     $last_month = date('Y') - 1 .'120000';
   }
   else {
-    $last_month = date('Y'). ($last_month < 10 ? '0' : '') . $last_month .'0000';
+    $last_month = date('Y') . ($last_month < 10 ? '0' : '') . $last_month .'0000';
   }
   $statistics['last_month']['views'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'view' AND date >= %d AND date <= %d", $aid, $last_month, $this_month));
   $statistics['last_month']['clicks'] = (int)db_result(db_query("SELECT SUM(count) FROM {ad_statistics} WHERE aid = %d AND action = 'click' AND date >= %d AND date <= %d", $aid, $last_month, $this_month));
@@ -424,7 +423,7 @@ function ad_statistics($aid) {
 function theme_ad_status_display($node) {
   $status_array = ad_status_array();
   $output  = '<div class="adstatus">';
-  $output .= '<p>'. t($status_array["$node->adstatus"]) .'</p>';
+  $output .= '<p>'. t($status_array[$node->adstatus]) .'</p>';
   switch ($node->adstatus) {
     case 'approved':
       if ($node->autoactivate) {
@@ -566,12 +565,10 @@ function ad_cron() {
  * @return  An array of permissions used by this module.
  */
 function ad_perm() {
-  return ( 
-          array('administer advertisements',
+  return array('administer advertisements',
                 'create advertisements',
                 'edit own advertisements',
-                'show advertisements')
-         );
+                'show advertisements');
 }
 
 /**
@@ -588,14 +585,12 @@ function ad_node_info() {
 /**
  */
 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) || 
+    return (user_access('administer advertisements', $account) ||
            (ad_is_owner($node->nid) && user_access('edit own advertisements', $account)));
   }
 }
@@ -603,7 +598,7 @@ function ad_access($op, $node, $account) {
 /**
  * Drupal _form hook.
  */
-function ad_form(&$node) {
+function ad_form(&$node, $form_state) {
   $form = array();
   $edit = $_POST['edit'];
 
@@ -622,14 +617,14 @@ function ad_form(&$node) {
 
   $form['title'] = array(
     '#type' => 'textfield',
-    '#title' => t('Title'), 
-    '#required' => TRUE, 
+    '#title' => t('Title'),
+    '#required' => TRUE,
     '#default_value' => $node->title,
   );
   $form['body_filter']['body'] = array(
-    '#type' => 'textarea', 
-    '#title' => t('Description'), 
-    '#default_value' => $node->body, 
+    '#type' => 'textarea',
+    '#title' => t('Description'),
+    '#default_value' => $node->body,
     '#rows' => 3
   );
   $form['body_filter']['format'] = filter_form($node->format);
@@ -648,11 +643,11 @@ function ad_form(&$node) {
         if (arg(0) == 'node' && arg(1) == 'add' && arg(2) == 'ad') {
           $adtype = arg(3) ? arg(3) : $edit['adtype'];
           $form['adtype'] = array(
-            '#type' => 'radios', 
-            '#title' => t('Style of ad'), 
-            '#options' => drupal_map_assoc($adtypes), 
-            '#default_value' => $adtype ? $adtype : $adtypes[0], 
-            '#required' => TRUE, 
+            '#type' => 'radios',
+            '#title' => t('Style of ad'),
+            '#options' => drupal_map_assoc($adtypes),
+            '#default_value' => $adtype ? $adtype : $adtypes[0],
+            '#required' => TRUE,
             '#description' => t('Select the type of ad that you wish to create from the above options.')
           );
         }
@@ -664,10 +659,10 @@ function ad_form(&$node) {
   if (isset($adtype)) {
     $elements = module_invoke('ad_'. $adtype, 'adapi', 'form', $node);
     foreach ($elements as $element => $values) {
-      $form["$element"] = $values;
+      $form[$element] = $values;
     }
     $form['adtype'] = array(
-      '#type' => 'hidden', 
+      '#type' => 'hidden',
       '#value' => $adtype,
     );
   }
@@ -675,18 +670,18 @@ function ad_form(&$node) {
   if (user_access('administer advertisements')) {
     // admins can set any status on advertisements
     $form['adstatus'] = array(
-      '#type' => 'fieldset', 
-      '#title' => t('Status'), 
+      '#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, 
+        '#type' => 'radio',
+        '#title' => t($status),
+        '#return_value' => $status,
         '#default_value' => $node->adstatus ? $node->adstatus : 'pending',
-        '#description' => "$description", 
-        '#parents' => array("adstatus")
+        '#description' => $description,
+        '#parents' => array('adstatus')
       );
     }
   }
@@ -695,16 +690,16 @@ function ad_form(&$node) {
       $adstatus = ad_status_array();
       $node->adstatus = 'pending';
       $form['adstatus'] = array(
-        '#type' => 'fieldset', 
-        '#title' => t('Status'), 
+        '#type' => 'fieldset',
+        '#title' => t('Status'),
         '#collapsible' => TRUE
       );
       $form['adstatus']['display'] = array(
-        '#type' => 'markup', 
-        '#value' => '<p><b>'. t('Status') .':</b> '. t($node->adstatus) .'<br />'. t($adstatus["$node->adstatus"]),
+        '#type' => 'markup',
+        '#value' => '<p><strong>'. t('Status') .':</strong> '. t($node->adstatus) .'<br />'. t($adstatus[$node->adstatus]),
       );
       $form['adstatus']['adpending'] = array(
-        '#type' => 'value', 
+        '#type' => 'value',
         '#value' => $node->adstatus
       );
     }
@@ -712,17 +707,17 @@ function ad_form(&$node) {
       $adstatus = ad_status_array(FALSE);
       // display status options
       $form['adstatus'] = array(
-        '#type' => 'fieldset', 
-        '#title' => t('Status'), 
+        '#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", 
+          '#type' => 'radio',
+          '#title' => t($status),
+          '#return_value' => $status,
+          '#default_value' => $node->adstatus ? $node->adstatus : 'pending',
+          '#description' => $description,
           '#parents' => array("adstatus")
         );
       }
@@ -734,16 +729,16 @@ function ad_form(&$node) {
       $node->adstatus = 'pending';
     }
     $form['ad_adstatus'] = array(
-      '#type' => 'fieldset', 
-      '#title' => t('Status'), 
+      '#type' => 'fieldset',
+      '#title' => t('Status'),
       '#collapsible' => TRUE
     );
     $form['ad_adstatus']['adstatus_display'] = array(
-      '#type' => 'markup', 
-      '#value' => '<p><b>'. t('Status') .':</b> '. t($node->adstatus) .'<br />'. t($adstatus["$node->adstatus"]),
+      '#type' => 'markup',
+      '#value' => '<p><strong>'. t('Status') .':</strong> '. t($node->adstatus) .'<br />'. t($adstatus[$node->adstatus]),
     );
     $form['adstatus'] = array(
-      '#type' => 'value', 
+      '#type' => 'value',
       '#value' => $node->adstatus
     );
   }
@@ -751,11 +746,11 @@ function ad_form(&$node) {
   if (ad_adaccess($node->nid, 'access statistics')) {
     // display statistics
     $form['statistics'] = array(
-      '#type' => 'fieldset', 
-      '#title' => t('Statistics'), 
+      '#type' => 'fieldset',
+      '#title' => t('Statistics'),
       '#collapsible' => TRUE,
     );
-  
+
     $form['statistics']['data'] = array(
       '#type' => 'markup',
       '#prefix' => '<div>',
@@ -763,32 +758,32 @@ function ad_form(&$node) {
       '#value' => theme('ad_statistics_display', ad_statistics($node->nid)),
     );
   }
-  
+
   // display scheduling options
   $form['schedule'] = array(
-    '#type' => 'fieldset', 
-    '#title' => t('Scheduling'), 
+    '#type' => 'fieldset',
+    '#title' => t('Scheduling'),
     '#collapsible' => TRUE,
     // Collapse if there isn't any scheduling data set.
     '#collapsed' => ($node->autoactivate || $edit['autoactivate'] ||
                      $node->autoexpire || $edit['autoexpire'] ||
                      $node->maxviews || $edit['maxviews'] ||
-                     $node->maxclicks || $edit['maxclicks']) 
+                     $node->maxclicks || $edit['maxclicks'])
                      ? FALSE : TRUE,
   );
 
   if (ad_adaccess($node->nid, 'manage status')) {
-    $form['schedule']['current'] = array( 
-      '#type' => 'markup', 
+    $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' => $node->autoactivate ? format_date((int)$node->autoactivate, 'custom', 'F j, Y H:i') : '', 
+    $form['schedule']['autoactivate'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Automatically activate ad'),
+      '#required' => FALSE,
+      '#default_value' => $node->autoactivate ? format_date((int)$node->autoactivate, 'custom', 'F j, Y H:i') : '',
       '#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.')
     );
   }
@@ -796,25 +791,25 @@ function ad_form(&$node) {
   if (user_access('administer advertisements')) {
     // admins can expire advertisements
     $form['schedule']['autoexpire'] = array(
-      '#type' => 'textfield', 
-      '#title' => t('Automatically expire ad'), 
-      '#required' => FALSE, 
-      '#default_value' => $node->autoexpire ? format_date((int)$node->autoexpire, 'custom', 'F j, Y H:i') : '', 
+      '#type' => 'textfield',
+      '#title' => t('Automatically expire ad'),
+      '#required' => FALSE,
+      '#default_value' => $node->autoexpire ? format_date((int)$node->autoexpire, 'custom', 'F j, Y H:i') : '',
       '#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 views'), 
-      '#required' => FALSE, 
+      '#type' => 'textfield',
+      '#title' => t('Maximum views'),
+      '#required' => FALSE,
       '#size' => 10,
       '#maxlength' => 11,
       '#default_value' => $node->maxviews,
       '#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 views, leave this field set to %zero.', array('%zero' => '0')),
     );
     $form['schedule']['maxclicks'] = array(
-      '#type' => 'textfield', 
-      '#title' => t('Maximum clicks'), 
-      '#required' => FALSE, 
+      '#type' => 'textfield',
+      '#title' => t('Maximum clicks'),
+      '#required' => FALSE,
       '#size' => 10,
       '#maxlength' => 11,
       '#default_value' => $node->maxclicks,
@@ -824,13 +819,13 @@ function ad_form(&$node) {
   else {
     // display expiration time
     $form['schedule']['autoexpire_display'] = array(
-      '#type' => 'markup', 
+      '#type' => 'markup',
       '#prefix' => '<div>',
       '#suffix' => '</div>',
       '#value' => theme('ad_status_display', $node),
     );
     $form['schedule']['autoexpire'] = array(
-      '#type' => 'hidden', 
+      '#type' => 'hidden',
       '#value' => $node->autoexpire
     );
   }
@@ -841,7 +836,7 @@ function ad_form(&$node) {
 /**
  * Drupal _form_alter(&$form, &$form_state, $form_id) hook.
  */
-function ad_form_alter($form_id, &$form) {
+function ad_form_alter(&$form, &$form_state, $form_id) {
   if ($form_id == 'taxonomy_form_vocabulary') {
     // Remove taxonomy form options not applicable for ad groups.
     if ($form['vid']['#value'] == _ad_get_vid()) {
@@ -972,7 +967,7 @@ function ad_nodeapi(&$node, $op, $teaser, $page) {
           $activated = time();
         }
         // Check if ad is being manually expired.
-        else if ($ad->status != 'expired' && $node->adstatus == 'expired') {
+        else if ($ad->adstatus != 'expired' && $node->adstatus == 'expired') {
           // Ad has been manually expired.
           $expired = time();
         }
@@ -986,7 +981,7 @@ function ad_nodeapi(&$node, $op, $teaser, $page) {
           ad_statistics_increment($node->nid, $node->adstatus);
         }
         // Update ads table with new information.
-        db_query("UPDATE {ads} SET uid = %d, adstatus = '%s', adtype = '%s', autoactivate = %d, autoexpire = %d, activated = %d, maxviews = %d, maxclicks = %d, expired = %d WHERE aid = %d", $node->uid, $node->adstatus, $node->adtype, $node->autoactivate ? strtotime($node->autoactivate) : '', $node->autoexpire ? strtotime($node->autoexpire) : '', $activated, (int)$node->maxviews, (int)$node->maxclicks, $expired, $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)), $node->autoactivate ? strtotime($node->autoactivate) : '', $node->autoexpire ? strtotime($node->autoexpire) : '', $activated, (int)$node->maxviews, (int)$node->maxclicks, $expired, $node->nid);
         // Be sure ad owner has at least default ad permissions.
         ad_owners_add($node->nid, $node->uid);
         ad_host_id_create($node->uid);
@@ -1006,7 +1001,7 @@ function ad_nodeapi(&$node, $op, $teaser, $page) {
         module_invoke_all('adowners', 'remove', $id->oid, $owner);
       }
       db_query('DELETE FROM {ad_owners} WHERE aid = %d', $node->nid);
-      // All that's left of the ad is a single timestamp as to when it was 
+      // All that's left of the ad is a single timestamp as to when it was
       // deleted.
       ad_statistics_increment($node->nid, 'delete');
       break;
@@ -1021,7 +1016,7 @@ function ad_nodeapi(&$node, $op, $teaser, $page) {
       }
       break;
   }
-  // Allow ad type module to act on nodeapi events.  The adapi hook provides 
+  // Allow ad type module to act on nodeapi events.  The adapi hook provides
   // access to additional variables not available in the nodeapi hook.
   if ($node->adtype) {
     // Don't use module_invoke, as in pre-PHP5 the changes to $node won't be
@@ -1057,7 +1052,7 @@ function theme_node_ad($node, $yield_form = TRUE) {
       $header[] = array('data' => t('Status'), 'field' => 'status');
     }
     $header[] = array('data' => t(' '));
- 
+
     if ($node->nid) {
       $sql = "SELECT cid, timestamp, uid, status, hostname, url FROM {ad_clicks} WHERE aid = $node->nid";
       $sql .= tablesort_sql($header);
@@ -1084,7 +1079,7 @@ function theme_node_ad($node, $yield_form = TRUE) {
         if (function_exists('click_filter_status_text') && user_access('view filtered clicks')) {
           $row[] = click_filter_status_text($ad->status);
         }
-        $row[] = '['. l('details', "node/$node->nid/details/$ad->cid") .']';
+        $row[] = '['. l(t('details'), "node/$node->nid/details/$ad->cid") .']';
         $rows[] = $row;
       }
 
@@ -1175,7 +1170,7 @@ function ad_menu() {
    Dynamic menu items under a '!$may_cache' block can often be simplified
    to remove references to arg(n) and use of '%<function-name>' to check
    conditions. See http://drupal.org/node/103114.
-   
+
    The title and description arguments should not have strings wrapped in t(),
    because translation of these happen in a later stage in the menu system.
 
@@ -1220,7 +1215,7 @@ function ad_menu() {
     'title' => 'List',
     'page callback' => 'ad_admin_groups_list',
     'access arguments' => array('administer advertisements'),
-    'type' => MENU_DEFAULT_LOCAL_TASK, 
+    'type' => MENU_DEFAULT_LOCAL_TASK,
     'weight' => 0);
   $items['admin/content/ad/groups/add'] = array(
     'title' => 'Create group',
@@ -1228,7 +1223,27 @@ function ad_menu() {
     'page arguments' => array('ad_admin_group_form'),
     'access arguments' => array('administer advertisements'),
     'type' => MENU_LOCAL_TASK,
-    'weight' => 3);
+    'weight' => 3
+  );
+  $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,
+  );
+  $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,
+  );
 
   // configure sub tabs
   $items['admin/content/ad/configure/global'] = array(
@@ -1237,7 +1252,8 @@ function ad_menu() {
     'page arguments' => array('ad_admin_configure_settings'),
     'access arguments' => array('administer advertisements'),
     'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => 0);
+    'weight' => 0
+  );
 
   $adtypes = module_invoke_all('adapi', 'type', array());
   foreach ($adtypes as $adtype) {
@@ -1256,54 +1272,27 @@ function ad_menu() {
     );
     // Node add items.
     $items['node/add/ad/'. $adtype] = array(
-     'title' => t('!type advertisement', array('!type' => $adtype)),
-     'access arguments' => array('create advertisements'),
+      'title' => t('!type advertisement', array('!type' => $adtype)),
+      'access arguments' => array('create advertisements'),
+      'file' => _ad_relative_path(realpath(dirname(__FILE__)), $_SERVER['DOCUMENT_ROOT'].base_path()). '/modules/node/node.pages.inc',
     );
   }
-  
-  
+
   $items["ad/redirect/%/%/%"] = array(
     'access arguments' => array('show advertisements'),
-    'type' => MENU_CALLBACK, 
+    '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)),
+  );
 /***
   // callbacks
-  if (arg(0) == 'ad' && arg(1) == 'redirect' && is_numeric(arg(2))) {
-    $aid = preg_replace('/[^0-9]/', '', arg(2));
-    $group = preg_replace('/[^0-9,nt]/', '', arg(3));
-    $hostid = preg_replace('/[^0-9a-f]/', '', arg(4));
-    $items["ad/redirect/$aid"] = array(
-      'access arguments' => array('show advertisements'),
-      'type' => MENU_CALLBACK, 
-      'page callback' => 'ad_redirect',
-      'page arguments' => array($aid, $group, $hostid)
-    );
-  }
-  elseif (arg(2) == 'ad' && arg(3) == 'groups' && is_numeric(arg(4))) {
-    if ($term = taxonomy_get_term(arg(4))) {
-      $items["admin/content/ad/groups/$term->tid/edit"] = array(
-        'title' => 'edit',
-        'page callback' => 'drupal_get_form',
-        'page arguments' => array(
-        'ad_admin_group_form', 
-        (array)$term),
-        'access arguments' => array('administer advertisements'),
-        'type' => MENU_CALLBACK,
-        'weight' => 1);
-      $items["admin/content/ad/groups/$term->tid/delete"] = array(
-        'title' => 'delete',
-        'page callback' => 'drupal_get_form',
-        'page arguments' => array(
-        'ad_confirm_group_delete', 
-        (array)$term),
-        'access arguments' => array('administer advertisements'),
-        'type' => MENU_CALLBACK,
-        'weight' => 2);
-    }
-  }
-  elseif (arg(0) == 'node' && is_numeric(arg(1)) && ad_adaccess(arg(1), 'manage owners')) {
+  if (arg(0) == 'node' && is_numeric(arg(1)) && ad_adaccess(arg(1), 'manage owners')) {
     $node = node_load(arg(1));
     if ($node->adtype) {
       $items["node/$node->nid/adowners"] = array(
@@ -1358,6 +1347,20 @@ function ad_menu() {
 }
 
 /**
+ * Drupal menu wildcard 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;
+}
+
+/**
  * Drupal _block hook.
  */
 function ad_block($op = 'list', $delta = 0, $edit = array()) {
@@ -1414,7 +1417,7 @@ function ad_add() {
       }
     }
     drupal_set_title(t('Submit %name', array('%name' => $adtype)));
-    $output = drupal_get_form('ad_node_form', $node);
+    $output = drupal_get_form('ad_'.$adtype.'_node_form', $node);
   }
   else {
     $output = t('Choose from the following available advertisement types:');
@@ -1490,7 +1493,7 @@ function ad_owners_overview($node) {
 /**
  * A simple form for adding new users as owners of ads.
  */
-function ad_owners_add_form($node) {
+function ad_owners_add_form($form_state, $node) {
   $form = array();
   drupal_set_title('Add owner');
 
@@ -1501,7 +1504,7 @@ function ad_owners_add_form($node) {
   $form['username'] = array(
     '#autocomplete_path' => 'user/autocomplete',
     '#description' => t('Enter the username of the user who should have ownership permissions on this advertisement.'),
-    '#required' => TRUE, 
+    '#required' => TRUE,
     '#type' => 'textfield',
     '#title' => t('Username'),
   );
@@ -1596,7 +1599,7 @@ function ad_owners_add($aid, $uid, $permissions = array()) {
 }
 
 /**
- * Display a form with all available permissions and their status for the 
+ * Display a form with all available permissions and their status for the
  * selected ad and ad owner.
  */
 function ad_owner_permissions($aid, $uid) {
@@ -1615,12 +1618,12 @@ function ad_owner_permissions($aid, $uid) {
   $node = node_load($aid);
   $permissions = module_invoke_all('adapi', 'permissions', $node);
   foreach ($permissions as $permission) {
-    $form['permission']["$permission"] = array(
-      '#value' => t("$permission"),
+    $form['permission'][$permission] = array(
+      '#value' => t($permission),
     );
-    $form['grant'][str_replace(' ', '_', "$permission")] = array(
+    $form['grant'][str_replace(' ', '_', $permission)] = array(
       '#type' => 'checkbox',
-      '#default_value' => in_array("$permission", $granted) ? 1 : 0,
+      '#default_value' => in_array($permission, $granted) ? 1 : 0,
     );
   }
 
@@ -1654,8 +1657,8 @@ function theme_ad_owner_permissions($form) {
   $output = drupal_render($form['options']);
   foreach (element_children($form['permission']) as $key) {
     $row = array();
-    $row[] = drupal_render($form['permission']["$key"]);
-    $row[] = drupal_render($form['grant'][str_replace(' ', '_', "$key")]);
+    $row[] = drupal_render($form['permission'][$key]);
+    $row[] = drupal_render($form['grant'][str_replace(' ', '_', $key)]);
     $rows[] = $row;
   }
 
@@ -1671,7 +1674,7 @@ function ad_owner_permissions_submit($form, &$form_state) {
   $permissions = module_invoke_all('adapi', 'permissions', array());
   $perms = array();
   foreach ($permissions as $permission) {
-    if ($form_state['values'][str_replace(' ', '_', "$permission")]) {
+    if ($form_state['values'][str_replace(' ', '_', $permission)]) {
       $perms[] = $permission;
     }
   }
@@ -1721,7 +1724,7 @@ function ad_adaccess($aid, $string, $account = NULL) {
     $permissions[$aid][$account->uid] = explode('|,|', db_result(db_query("SELECT permissions FROM {ad_permissions} WHERE oid = %d", $oid)));
   }
 
-  return (in_array("$string", $permissions[$aid][$account->uid]));
+  return (in_array($string, $permissions[$aid][$account->uid]));
 }
 
 /**
@@ -1777,8 +1780,8 @@ function ad_admin_ads() {
     $ads[$ad->aid] = '';
     $form['title'][$ad->aid] = array('#value' => l($ad->title, 'node/'. $ad->aid));
     $form['group'][$ad->aid] = array('#value' => _ad_get_group($ad->aid));
-    $form['adtype'][$ad->aid] = array('#value' => $ad->adtype);
-    $form['adstatus'][$ad->aid] = array('#value' => $ad->adstatus);
+    $form['adtype'][$ad->aid] = array('#value' => t($ad->adtype));
+    $form['adstatus'][$ad->aid] = array('#value' => t($ad->adstatus));
     $form['operations'][$ad->aid] = array('#value' => l(t('edit'), 'node/'. $ad->aid .'/edit', array('query' => $destination)));
   }
   $form['ads'] = array('#type' => 'checkboxes', '#options' => $ads);
@@ -1847,7 +1850,7 @@ function ad_operations_callback(&$form_state, $ads, $action) {
     ad_host_id_create($node->uid);
     ad_statistics_increment($aid, 'update');
     ad_statistics_increment($aid, $action);
-    // Allow ad type module to act on nodeapi events.  The adapi hook provides 
+    // Allow ad type module to act on nodeapi events.  The adapi hook provides
     // access to additional variables not available in the nodeapi hook.
     if ($node->adtype) {
       // Don't use module_invoke, as in pre-PHP5 the changes to $node won't be
@@ -1899,7 +1902,7 @@ function ad_multiple_delete_confirm_submit($form, &$form_state) {
     }
     drupal_set_message(t('The ads have been deleted.'));
   }
-  return 'admin/content/ad';
+  $form_state['redirect'] = 'admin/content/ad';
 }
 
 /**
@@ -2065,7 +2068,7 @@ function ad_filters() {
 /**
  * Return form for advertisement administration filters.
  */
-function ad_filter_form() {
+function ad_filter_form($form_state) {
   $session = &$_SESSION['ad_overview_filter'];
   $session = is_array($session) ? $session : array();
   $filters = ad_filters();
@@ -2113,7 +2116,7 @@ function ad_filter_form() {
             // two opposite keys will always return 0 results.
             $inverse = $key == 1 ? 0 : 1;
             unset($filters['status']['options'][$option[1]]);
-            unset($filters['status']['options']["$value-$inverse"]);
+            unset($filters['status']['options'][$value.'-'.$inverse]);
           }
         }
       }
@@ -2188,7 +2191,7 @@ function ad_admin_statistics() {
   foreach ($groups as $tid => $group) {
     if ($tid) {
       $ads = db_result(db_query("SELECT count(aid) FROM {ads} a JOIN {term_node} t ON a.aid = t.nid WHERE t.tid = %d AND adstatus = 'active'", $tid));
-      $filter = "= $tid";
+      $filter = '= ' . $tid;
     }
     else {
       $ads = db_result(db_query("SELECT count(aid) FROM {ads} a LEFT JOIN {term_node} t ON a.aid = t.nid WHERE t.tid IS NULL AND adstatus = 'active'"));
@@ -2205,17 +2208,17 @@ function ad_admin_statistics() {
     );
 
     // Get overall global statistics.
-    $statistics['global']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND n.tid $filter"));
-    $statistics['global']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND n.tid $filter"));
+    $statistics['global']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND n.tid %s", $filter));
+    $statistics['global']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND n.tid %s", $filter));
 
     // Get overall statistics for this year and last year.
     $this_year = date('Y000000');
     $last_year = date('Y') - 1 .'000000';
-    $statistics['last_year']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND date <= %d AND n.tid $filter", $last_year, $this_year));
-    $statistics['last_year']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND date <= %d AND n.tid $filter", $last_year, $this_year));
-    $statistics['this_year']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid $filter", $this_year));
-    $statistics['this_year']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid $filter", $this_year));
-  
+    $statistics['last_year']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND date <= %d AND n.tid %s", $last_year, $this_year, $filter));
+    $statistics['last_year']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND date <= %d AND n.tid %s", $last_year, $this_year, $filter));
+    $statistics['this_year']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid %s", $this_year, $filter));
+    $statistics['this_year']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid %s", $this_year, $filter));
+
     // Get statistics for this month and last month.
     $this_month = date('Ym0000');
     $last_month = date('m') - 1;
@@ -2225,38 +2228,38 @@ function ad_admin_statistics() {
     else {
       $last_month = date('Y'). ($last_month < 10 ? '0' : '') . $last_month .'0000';
     }
-    $statistics['last_month']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND date <= %d AND n.tid $filter", $last_month, $this_month));
-    $statistics['last_month']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND date <= %d AND n.tid $filter", $last_month, $this_month));
-    $statistics['this_month']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid $filter", $this_month));
-    $statistics['this_month']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid $filter", $this_month));
-  
+    $statistics['last_month']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND date <= %d AND n.tid %s", $last_month, $this_month, $filter));
+    $statistics['last_month']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND date <= %d AND n.tid %s", $last_month, $this_month, $filter));
+    $statistics['this_month']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid %s", $this_month, $filter));
+    $statistics['this_month']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid %s", $this_month, $filter));
+
     // Get statistics for this week.
     $this_week_start = date('Ymd00', time() - 60*60*24*6);
-    $statistics['this_week']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid $filter", $this_week_start));
-    $statistics['this_week']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid $filter", $this_week_start));
-  
+    $statistics['this_week']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid %s", $this_week_start, $filter));
+    $statistics['this_week']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid %s", $this_week_start, $filter));
+
     // Get statistics for yesterday and today.
     $yesterday_start = date('Ymd00', time() - 60*60*24);
     $yesterday_end = date('Ymd24', time() - 60*60*24);
     $today_start = date('Ymd00', time());
-    $statistics['yesterday']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND date <= %d AND n.tid $filter", $yesterday_start, $yesterday_end));
-    $statistics['yesterday']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND date <= %d AND n.tid $filter", $yesterday_start, $yesterday_end));
-    $statistics['today']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid $filter", $today_start));
-    $statistics['today']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid $filter", $today_start));
-  
+    $statistics['yesterday']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND date <= %d AND n.tid %s", $yesterday_start, $yesterday_end, $filter));
+    $statistics['yesterday']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND date <= %d AND n.tid %s", $yesterday_start, $yesterday_end, $filter));
+    $statistics['today']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date >= %d AND n.tid %s", $today_start, $filter));
+    $statistics['today']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date >= %d AND n.tid %s", $today_start, $filter));
+
     // Get statistics for this hour and the last hour.
     $last_hour = date('YmdH', time() - 60*60);
     $this_hour = date('YmdH', time());
-    $statistics['last_hour']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date = %d AND n.tid $filter", $last_hour));
-    $statistics['last_hour']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date = %d AND n.tid $filter", $last_hour));
-    $statistics['this_hour']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date = %d AND n.tid $filter", $this_hour));
-    $statistics['this_hour']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date = %d AND n.tid $filter", $this_hour));
+    $statistics['last_hour']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date = %d AND n.tid %s", $last_hour, $filter));
+    $statistics['last_hour']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date = %d AND n.tid %s", $last_hour, $filter));
+    $statistics['this_hour']['views'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'view' AND date = %d AND n.tid %s", $this_hour, $filter));
+    $statistics['this_hour']['clicks'] = (int)db_result(db_query("SELECT SUM(s.count) FROM {ad_statistics} s LEFT JOIN {ads} a ON s.aid = a.aid LEFT JOIN {term_node} n ON a.aid = n.nid WHERE action = 'click' AND date = %d AND n.tid %s", $this_hour, $filter));
 
     // TODO: Create this view and remove the && FALSE to enable this code.
     if (module_exists('views') && FALSE) {
       $form[$group->name]['statistics'] = array(
         '#type' => 'markup',
-        '#value' => '<p>'. t('There %count in this group.', array('%count' => format_plural($ads, 'is '. l('1 active ad', "ad/$group->gid/group"), 'are '. l('%count active ads', "ad/$group->tid/group")))) .'</p>' . theme('ad_statistics_display', $statistics),
+        '#value' => '<p>'. t('There %count in this group.', array('%count' => format_plural($ads, 'is '. l('1 active ad', "ad/". $group->gid ."/group"), 'are '. l('%count active ads', "ad/$group->tid/group")))) .'</p>' . theme('ad_statistics_display', $statistics),
       );
     }
     else {
@@ -2297,7 +2300,7 @@ function ad_admin_configure_settings($edit = array()) {
   );
 
   $form['general'] = array(
-    '#type' => 'fieldset', 
+    '#type' => 'fieldset',
     '#title' => t('General'),
     '#collapsible' => TRUE,
     '#collapsed' => FALSE,
@@ -2309,9 +2312,9 @@ function ad_admin_configure_settings($edit = array()) {
     '#type' => 'radios',
     '#title' => t('Click-through target'),
     '#options' => array(
-      '_self' => t('same browser window and frame'), 
-      '_blank' => t('new browser window'), 
-      '_parent' => t('parent frame'), 
+      '_self' => t('same browser window and frame'),
+      '_blank' => t('new browser window'),
+      '_parent' => t('parent frame'),
       '_top' => t('same browser window, removing all frames'),
     ),
     '#default_value' => variable_get('ad_link_target', '_self'),
@@ -2336,50 +2339,50 @@ function ad_admin_configure_settings($edit = array()) {
   }
 
   $form['general']['ad_display'] = array(
-    '#type' => 'radios', 
-    '#title' => t('Display type'), 
-    '#default_value' => variable_get('ad_display', 'javascript'), 
+    '#type' => 'radios',
+    '#title' => t('Display type'),
+    '#default_value' => variable_get('ad_display', 'javascript'),
     '#options' => $display_options,
     '#description' => $description,
   );
 
   $form['general']['ad_validate_url'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Validate URLs'), 
-    '#default_value' => variable_get('ad_validate_url', 1), 
+    '#title' => t('Validate URLs'),
+    '#default_value' => variable_get('ad_validate_url', 1),
     '#description' => t('If enabled, any destination URLs entered in ads will be required to be complete URLs (including http:// or https:// at the beginning).  If you wish to include internal urls, you will need to disable this option.'),
   );
 
   $form['general']['ad_filter'] = array(
     '#type' => 'checkbox',
-    '#title' => t('Filter ads'), 
-    '#default_value' => variable_get('ad_filter', 0), 
+    '#title' => t('Filter ads'),
+    '#default_value' => variable_get('ad_filter', 0),
     '#description' => t('If enabled, the input format for each advertisement node will be applied to the displayed advertisement.'),
   );
 
   $form['iframe'] = array(
-    '#type' => 'fieldset', 
+    '#type' => 'fieldset',
     '#title' => t('IFrame'),
     '#collapsible' => TRUE,
     '#collapsed' => variable_get('ad_display', 'javascript') == 'iframe' ? FALSE : TRUE
   );
   $form['iframe']['ad_iframe_frameborder'] = array(
-    '#type' => 'checkbox', 
+    '#type' => 'checkbox',
     '#title' => t('Frameborder'),
-    '#default_value' => variable_get('ad_iframe_frameborder', 0), 
+    '#default_value' => variable_get('ad_iframe_frameborder', 0),
     '#description' => t('If enabled, IFrames used for displaying ads will have a frameborder.'),
   );
   $form['iframe']['ad_iframe_scroll'] = array(
-    '#type' => 'radios', 
-    '#title' => t('Scrolling'), 
-    '#default_value' => variable_get('ad_iframe_scroll', 'auto'), 
+    '#type' => 'radios',
+    '#title' => t('Scrolling'),
+    '#default_value' => variable_get('ad_iframe_scroll', 'auto'),
     '#options' => array('auto' => 'auto', 'on' => 'on', 'off' => 'off'),
     '#description' => t('Define whether or not scroll bars should be enabled for the ad IFrame.'),
   );
   $form['iframe']['ad_iframe_width'] = array(
     '#type' => 'textfield',
     '#title' => t('Width'),
-    '#default_value' => variable_get('ad_iframe_width', ''), 
+    '#default_value' => variable_get('ad_iframe_width', ''),
     '#maxlength' => 8,
     '#size' => 5,
     '#required' => FALSE,
@@ -2388,7 +2391,7 @@ function ad_admin_configure_settings($edit = array()) {
   $form['iframe']['ad_iframe_height'] = array(
     '#type' => 'textfield',
     '#title' => t('Height'),
-    '#default_value' => variable_get('ad_iframe_height', ''), 
+    '#default_value' => variable_get('ad_iframe_height', ''),
     '#maxlength' => 8,
     '#size' => 5,
     '#required' => FALSE,
@@ -2396,7 +2399,7 @@ function ad_admin_configure_settings($edit = array()) {
   );
 
   $form['cache'] = array(
-    '#type' => 'fieldset', 
+    '#type' => 'fieldset',
     '#title' => t('Cache'),
     '#collapsible' => TRUE,
     '#collapsed' => variable_get('ad_cache', 'none') == 'none' ? TRUE : FALSE,
@@ -2413,9 +2416,9 @@ function ad_admin_configure_settings($edit = array()) {
   }
 
   $form['cache']['ad_cache'] = array(
-    '#type' => 'radios', 
-    '#title' => t('Type'), 
-    '#default_value' => variable_get('ad_cache', 'none'), 
+    '#type' => 'radios',
+    '#title' => t('Type'),
+    '#default_value' => variable_get('ad_cache', 'none'),
     '#options' => $cache_options,
     '#description' => $description,
   );
@@ -2435,7 +2438,7 @@ function ad_admin_configure_settings($edit = array()) {
  * Validate form settings, calling attention to any illogical configurations.
  */
 function ad_admin_configure_settings_validate($form, &$form_state) {
-  if ($form_state['values']['ad_link_target'] == '_self' && 
+  if ($form_state['values']['ad_link_target'] == '_self' &&
       $form_state['values']['ad_display'] == 'iframe') {
     // We don't consider this an error, as this could be exactly what the
     // administrator is trying to do.  But as for most people it is likely
@@ -2492,15 +2495,17 @@ function ad_groups_list($object = FALSE, $tid = NULL) {
       $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]->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) {
@@ -2517,7 +2522,7 @@ function ad_groups_list($object = FALSE, $tid = NULL) {
       $tids = taxonomy_get_tree(_ad_get_vid());
       if (is_array($tids)) {
         foreach ($tids as $group) {
-          $names[$group->tid] = "$group->name";
+          $names[$group->tid] = $group->name;
         }
       }
       // Hard coded "default" group with tid of 0.
@@ -2591,7 +2596,8 @@ function ad_owner_remove_validate($form, &$form_state) {
   if ($node->uid == $form_state['values']['uid']) {
     $owner = user_load(array('uid' => $form_state['values']['uid']));
     drupal_set_message(t('%name is the primary owner of this advertisement.  You cannot remove the primary owner.', array('%name' => $owner->name)), 'error');
-    drupal_goto('node/'. $form_state['values']['aid'] .'/adowners');
+
+    $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/adowners';
   }
 }
 
@@ -2605,7 +2611,8 @@ function ad_owner_remove_submit($form, &$form_state) {
   $owner = user_load(array('uid' => $form_state['values']['uid']));
   module_invoke_all('adowners', 'remove', $oid, $owner);
   drupal_set_message(t('The ad owner %name has been removed.', array('%name' => $owner->name)));
-  drupal_goto('node/'. $form_state['values']['aid'] .'/adowners');
+
+  $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/adowners';
 }
 
 /**
@@ -2755,7 +2762,7 @@ function _ad_check_install() {
 
   _ad_get_vid();
   // Preserve old ad groups, if any.
-  if (($old = variable_get('ad_group_vid_restore', '')) && 
+  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);
@@ -2768,8 +2775,8 @@ function _ad_check_install() {
   }
 
   // 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 
+  // 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
@@ -2791,8 +2798,8 @@ function _ad_check_install() {
         // weight is an optional, defaults to 0
         $weight = $action['weight'] = 0;
       }
-      $actions["$weight.$name"] = $action;
-      $actions["$weight.$name"]['name'] = $name;
+      $actions[$weight. '.' .$name] = $action;
+      $actions[$weight. '.' .$name]['name'] = $name;
     }
     // order actions by weight (multiple same-weight actions sorted by alpha)
     ksort($actions);
@@ -2812,7 +2819,16 @@ function _ad_get_vid() {
     $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = '%s'", 'ad'));
     if (!$vid) {
       // No vid, so we create one.
-      $edit = array('name' => 'Ad groups', 'multiple' => 1, 'required' => 0, 'hierarchy' => 0, 'relations' => 0, 'module' => 'ad', 'nodes' => array('ad' => 1));
+      $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'];
     }
@@ -2825,15 +2841,15 @@ function _ad_get_vid() {
 /**
  * Returns a form for adding an ad group.
  */
-function ad_admin_group_form($group = array()) {
+function ad_admin_group_form($form_state, $group = NULL) {
   if ($_POST['op'] == t('Delete') || $_POST['edit']['confirm']) {
-    drupal_goto('admin/content/ad/groups/'. $group['tid'] .'/delete');
+    drupal_goto('admin/content/ad/groups/'. $group->tid .'/delete');
   }
 
   $form['name'] = array(
     '#type' => 'textfield',
     '#title' => t('Group name'),
-    '#default_value' => $group['name'],
+    '#default_value' => $group->name,
     '#maxlength' => 64,
     '#required' => TRUE,
     '#description' => t('Specify a name for the ad group.')
@@ -2842,17 +2858,17 @@ function ad_admin_group_form($group = array()) {
   $form['description'] = array(
     '#type' => 'textarea',
     '#title' => t('Description'),
-    '#default_value' => $group['description'],
+    '#default_value' => $group->description,
     '#required' => TRUE,
     '#description' => t('Describe this ad group.')
   );
-    
+
   $form['parent']['#tree'] = FALSE;
 
   $form['weight'] = array(
     '#type' => 'weight',
     '#title' => t('Weight'),
-    '#default_value' => $group['weight'],
+    '#default_value' => $group->weight,
     '#description' => t('When listing ad groups, those with lighter (smaller) weights get listed before ad groups with heavier (larger) weights.  Ad groups with equal weights are sorted alphabetically.')
   );
 
@@ -2861,16 +2877,16 @@ function ad_admin_group_form($group = array()) {
     '#value' => _ad_get_vid());
 
   $form['submit'] = array(
-    '#type' => 'submit', 
+    '#type' => 'submit',
     '#value' => t('Submit'));
 
-  if ($group['tid']) {
+  if ($group->tid) {
     $form['delete'] = array(
-      '#type' => 'submit', 
+      '#type' => 'submit',
       '#value' => t('Delete'));
     $form['tid'] = array(
-      '#type' => 'value', 
-      '#value' => $group['tid']);
+      '#type' => 'value',
+      '#value' => $group->tid);
   }
 
   return $form;
@@ -2891,22 +2907,22 @@ function ad_admin_group_form_submit($form, &$form_state) {
     case SAVED_UPDATED:
       drupal_set_message(t('The ad group %term has been updated.', array('%term' => $form_state['values']['name'])));
   }
-  return 'admin/content/ad/groups';
+  $form_state['redirect'] = 'admin/content/ad/groups';
 }
 
 /**
  * Returns a confirmation page when deleting an ad group and all of its ads.
  */
-function ad_confirm_group_delete($term) {
-  $form['tid'] = array('#type' => 'value', '#value' => $term['tid']);
-  $form['name'] = array('#type' => 'value', '#value' => $term['name']);
+function ad_confirm_group_delete($form_state, $group = NULL) {
+  $form['tid'] = array('#type' => 'value', '#value' => $group->tid);
+  $form['name'] = array('#type' => 'value', '#value' => $group->name);
 
   return confirm_form(
     $form,
-    t('Are you sure you want to delete the ad group %name?', array('%name' => $term['name'])), 
-    'admin/content/ad/group', 
-    t('Ads that were within this group will not be deleted.  This action cannot be undone.'), 
-    t('Delete'), 
+    t('Are you sure you want to delete the ad group %name?', array('%name' => $group->name)),
+    'admin/content/ad/group',
+    t('Ads that were within this group will not be deleted.  This action cannot be undone.'),
+    t('Delete'),
     t('Cancel'));
 }
 
@@ -2918,7 +2934,7 @@ function ad_confirm_group_delete_submit($form, &$form_state) {
   drupal_set_message(t('The ad group %term has been deleted.', array('%term' => $form_state['values']['name'])));
   watchdog('ad', 'mailarchive: deleted %term ad group.', array('%term' => $form_state['values']['name']));
 
-  return 'admin/content/ad/groups';
+  $form_state['redirect'] = 'admin/content/ad/groups';
 }
 
 /**
@@ -2942,7 +2958,7 @@ function ad_display_image($ad, $css = TRUE) {
   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" />';
+  $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>';
   }
@@ -2973,3 +2989,51 @@ function _ad_get_group($nid) {
   return $groups[$nid];
 }
 
+/**
+ * Retrieve the relative path between folders.  Basically the opposite of the
+ * realpath() function.
+ */
+function _ad_relative_path($start_dir, $final_dir, $dirsep = DIRECTORY_SEPARATOR) {
+
+  // Directory separator consistency
+  $start_dir = str_replace('/',$dirsep,$start_dir);
+  $final_dir = str_replace('/',$dirsep,$final_dir);
+  $start_dir = str_replace('\\',$dirsep,$start_dir);
+  $final_dir = str_replace('\\',$dirsep,$final_dir);
+
+  // Explode
+  $firstPathParts = explode($dirsep, $start_dir);
+  $secondPathParts = explode($dirsep, $final_dir);
+
+  // Get the number of parts that are the same.
+  $sameCounter = 0;
+  for ($i = 0; $i < min (count($firstPathParts), count($secondPathParts)); $i++) {
+    if (strtolower($firstPathParts[$i]) !== strtolower($secondPathParts[$i])) {
+      break;
+    }
+    $sameCounter++;
+  }
+  // If they do not share any common directories/roots, just return 2nd path.
+  if ($sameCounter == 0) {
+    return $final_dir;
+  }
+  $newPath = '';
+  // Go up the directory structure count(firstpathparts)-sameCounter times 
+  // (so, go up number of non-matching parts in the first path.)
+  for ($i = $sameCounter; $i < count($firstPathParts); $i++) {
+    if ($i > $sameCounter) {
+      $newPath .= $dirsep;
+    }
+    $newPath .= "..";
+  }
+  // If we did not have to go up at all, we're still in start_dir.
+  if (strlen($newPath) == 0) {
+    $newPath = ".";
+  }
+  // Now we go down as much as needed to get to final_dir.
+  for ($i = $sameCounter; $i < count($secondPathParts); $i++) {
+    $newPath .= $dirsep;
+    $newPath .= $secondPathParts[$i];
+  }
+  return $newPath;
+}
diff --git a/html/ad_html.module b/html/ad_html.module
index 9ead069..7f4f351 100644
--- a/html/ad_html.module
+++ b/html/ad_html.module
@@ -60,6 +60,9 @@ function ad_html_adapi($op, &$node) {
     case 'view':
       return ad_html_node_view($node);
 
+    case 'redirect':
+      return db_result(db_query('SELECT url FROM {ad_html} WHERE aid = %d', $node->nid));
+
     case 'type':
       return 'html';
 
diff --git a/statistics/click_filter/click_filter.install b/statistics/click_filter/click_filter.install
index 2fe1c50..d81d0fe 100644
--- a/statistics/click_filter/click_filter.install
+++ b/statistics/click_filter/click_filter.install
@@ -1,5 +1,5 @@
 <?php
-// $Id: click_filter.install,v 1.1.2.3.2.2 2008/10/31 01:58:48 jeremy Exp $
+// $Id: click_filter.install,v 1.1.2.3.2.1 2008/08/11 21:15:27 jeremy Exp $
 
 /**
  * Click_filter module database schema.
@@ -32,7 +32,7 @@ function click_filter_install() {
       }
       // Search for clicks by one of the ad's owners.
       $result = db_query('SELECT oid FROM {ad_owners} WHERE aid = %d AND uid = %d', $ad->aid, $click->uid);
-      if (db_fetch_object($result)) {
+      if (db_result($result)) {
         // Mark as clicked by ad owner.
         db_query('UPDATE {ad_clicks} SET status = %d WHERE cid = %d', CLICK_OWNER, $click->cid);
         // Decrement click from ad_statistics table.
@@ -41,8 +41,8 @@ function click_filter_install() {
         continue;
       }
       // Search for duplicate clicks.
-      $result = db_query("SELECT cid FROM {ad_clicks} WHERE aid = %d AND status = %d AND cid <> %d AND (uid = %d OR hostname = '%s')", $ad->aid, CLICK_VALID, $click->cid, $click->uid, $click->hostname);
-      if (db_fetch_object($result)) {
+      $result = db_query("SELECT cid FROM {ad_clicks} WHERE aid = %d AND status = %d AND cid != %d AND (uid = %d OR hostname = '%s')", $ad->aid, CLICK_VALID, $click->cid, $click->uid, $click->hostname);
+      if (db_result($result)) {
         // Mark as duplicate click.
         db_query('UPDATE {ad_clicks} SET status = %d WHERE cid = %d', CLICK_DUPLICATE, $click->cid);
         // Decrement click from ad_statistics table.
diff --git a/statistics/click_filter/click_filter.module b/statistics/click_filter/click_filter.module
index d3c7727..022ff84 100644
--- a/statistics/click_filter/click_filter.module
+++ b/statistics/click_filter/click_filter.module
@@ -1,8 +1,8 @@
 <?php
-// $Id: click_filter.module,v 1.1.2.3.2.2 2008/11/19 17:58:18 jeremy Exp $
+// $Id: click_filter.module,v 1.1.2.3.2.1 2008/08/11 21:15:27 jeremy Exp $
 
 /**
- * @file
+* @file
  * Filter duplicate and other unwanted clicks from ad statistics.
  *
  * Copyright (c) 2008.
@@ -66,7 +66,7 @@ function click_filter_status($aid, $hostid) {
   }
   // See if the click came from an owner of the ad.
   $result = db_query('SELECT oid FROM {ad_owners} WHERE aid = %d AND uid = %d', $aid, $user->uid);
-  if (db_fetch_object($result)) {
+  if (db_result($result)) {
     return CLICK_OWNER;
   }
   // See if the click came from a duplicate uid or ip address.
@@ -76,7 +76,7 @@ function click_filter_status($aid, $hostid) {
   else {
     $result = db_query("SELECT cid FROM {ad_clicks} WHERE aid = %d AND status = %d AND hostname = '%s'", $aid, CLICK_VALID, ip_address());
   }
-  if (db_fetch_object($result)) {
+  if (db_result($result)) {
     return CLICK_DUPLICATE;
   }
   return CLICK_VALID;
