diff notify/ad_notify.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/notify/ad_notify.module	Fri Feb 20 14:04:09 2009 +0000
+++ b/notify/ad_notify.module	Thu Apr 02 15:28:21 2009 +0000
@@ -1,11 +1,11 @@
 <?php
-// $Id: ad_notify.module,v 1.1.2.2.2.13.2.6 2009/02/16 23:12:29 jeremy Exp $
+// $Id: ad_notify.module,v 1.1.2.2.2.13.2.6.2.6 2009/03/29 20:17:21 jeremy Exp $
 
 /**
  * @file
- * Receive email notifications regarding ads.
+ * Automatically generate email notifications.
  *
- * Copyright (c) 2005-2008.
+ * Copyright (c) 2005-2009.
  *   Jeremy Andrews <jeremy@tag1consulting.com>.
  */
 
@@ -30,31 +30,47 @@
  */
 function ad_notify_menu() {
   $items = array();
-  
-  $items['node/%node/adowners/%user/notifications'] = array(
-    'title callback' => 'owner_notifications_title',
-    'title arguments' => array('!owner' => 3),
+
+  $items['admin/content/ad/notifications'] = array(
+    'title' => 'Global notifications',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('ad_notify_overview_form', 1, 3),
-    'access callback' => 'ad_adaccess',
-    'access arguments' => array(1, 'manage owners'),
+    'page arguments' => array('ad_notify_overview_form', '0', '0'),
+    'access arguments' => array('administer advertisements'),
     'type' => MENU_LOCAL_TASK,
-    'weight' => 4,
+    'weight' => 6,
   );
+  $items['admin/content/ad/notifications/%ad_notification/delete'] = array(
+    'title' => 'Delete notification',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ad_notify_confirm_delete', '0', '0', 4),
+    'access arguments' => array('administer advertisements'),
+    'weight' => 2,
+  );
+
   $items['node/%node/notifications'] = array(
     'title' => 'My notifications',
     'page callback' => 'drupal_get_form',
     'page arguments' => array('ad_notify_overview_form', 1),
-    'access callback' => 'ad_adaccess',
-    'access arguments' => array(1, array('manage owners', 'manage own notifications')),
+    'access callback' => 'ad_notify_tab_access',
+    'access arguments' => array(1),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 4,
+  );
+  $items['node/%node/adowners/%user/notifications'] = array(
+    'title callback' => 'owner_notifications_title',
+    'title arguments' => array(3),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ad_notify_overview_form', 1, 3),
+    'access callback' => 'ad_permission',
+    'access arguments' => array(1, 'manage owners'),
     'type' => MENU_LOCAL_TASK,
     'weight' => 4,
   );
   $items['node/%node/adowners/%user/notifications/%ad_notification/delete'] = array(
     'title' => 'Delete notification',
-    'page callback' => 'ad_notify_confirm_delete_page',
-    'page arguments' => array(1, 3, 5),
-    'access callback' => 'ad_adaccess',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('ad_notify_confirm_delete', 1, 3, 5),
+    'access callback' => 'ad_permission',
     'access arguments' => array(1, array('manage owners', 'manage own notifications')),
     'type' => MENU_CALLBACK,
   );
@@ -63,21 +79,28 @@
 }
 
 /**
- * Menu item title callback - use the user name
+ * Menu item title callback - use the user name.
  */
 function owner_notifications_title($account) {
-  return t('!owner\'s notifications', array('!owner' => $account->name));
+  if (isset($account) && is_object($account)) {
+    return t('!owner\'s notifications', array('!owner' => $account->name));
+  }
+}
+
+function ad_notify_tab_access($node) {
+  if (isset($node->adtype)) {
+    return (ad_permission($node->nid, 'manage owners') || ad_permission($node->nid, 'manage own notifications'));
+  }
 }
 
 /**
  * Implementation of hook_cron().
- * For performance reasons, all notifications are actually sent via this cron 
- * hook.
+ * Send time based notifications and those, who has negative delay.
  */
 function ad_notify_cron() {
   // Walk through all configured notifications and determine if any need to be
-  // emailed. 
-  $result = db_query('SELECT n.notid, o.aid, n.oid, n.event, n.queued, n.delay, n.sent, n.expire, n.address, n.subject, n.body FROM {ad_notify} n INNER JOIN {ad_owners} o ON n.oid = o.oid WHERE n.status = %d', AD_NOTIFY_ENABLED);
+  // emailed.
+  $result = db_query('SELECT n.notid, o.aid, o.uid, n.oid, n.event, n.queued, n.delay, n.sent, n.expire, n.address, n.subject, n.body, n.roles FROM {ad_notify} n INNER JOIN {ad_owners} o ON n.oid = o.oid WHERE n.status = %d', AD_NOTIFY_ENABLED);
   while ($notification = db_fetch_object($result)) {
     $send = FALSE;
     // Handle special case 'regular' notification that is simply a time-based
@@ -88,7 +111,7 @@
         $count = 1;
       }
     }
-    // Handle event based notifications based on information stored in the 
+    // Handle event based notifications based on information stored in the
     // ad_statistics table.
     else {
       if (($event = trim($notification->event, '-')) != $notification->event) {
@@ -120,7 +143,7 @@
     if ($send) {
       ad_notify_send_mail($notification, $count);
       if ($notification->expire) {
-        // Update the sent timestamp and counter, and auto-expire the 
+        // Update the sent timestamp and counter, and auto-expire the
         // notification so it is not sent again.
         db_query('UPDATE {ad_notify} SET queued = 0, sent = %d, counter = counter + 1, status = %d WHERE notid = %d', time(), AD_NOTIFY_DISABLED, $notification->notid);
       }
@@ -132,79 +155,45 @@
   }
 }
 
+function ad_notify_mail($key, &$message, $params) {
+  $language = $message['language'];
+  $message['subject'] = $params['notification']->subject;
+  $message['body'] = $params['notification']->body;
+}
+
 /**
- * Send email notifications using PHP mail() function.
+ * Send email notifications.
  */
-function ad_notify_send_mail($notification, $count = 0) {
-  $uid = db_result(db_query('SELECT uid FROM {ad_owners} WHERE oid = %d', $notification->oid));
-  $node = node_load(array('nid' => $notification->aid));
-  $owner = user_load(array('uid' => $uid));
-  $statistics = ad_statistics($notification->aid);
-  $notifications = module_invoke_all('adnotifyapi', 'register');
-  $variables = array(
-    '%owner_name' => $owner->name,
-    '%owner_mail' => $owner->mail,
-    '%owner_uid' => $owner->uid,
-    '%sitename' => variable_get('site_name', 'drupal'),
-    '%status' => $node->adstatus,
-    '%type' => $node->adtype,
-    '%event' => $notification->event,
-    '%frequency' => t(strtolower($notifications[$notification->event]), array('@when' => format_interval($notification->delay))),
-    '%redirect' => url($node->redirect, array('absolute' => TRUE)),
-    '%aid' => $notification->aid,
-    '%title' => $node->title,
-    '%url' => url("node/$node->nid", array('absolute' => TRUE)),
-    '%siteurl' => url('', array('absolute' => TRUE)),
-    '%comments' => $node->comment_count,
-    '%count' => $count,
-    '%created_small' => format_date($node->created, 'small'),
-    '%created_medium' => format_date($node->created, 'medium'),
-    '%created_large' => format_date($node->created, 'large'),
-    '%activated_small' => $node->activated ? format_date($node->activated, 'small') : t('never'),
-    '%activated_medium' => $node->activated ? format_date($node->activated, 'medium') : t('never'),
-    '%activated_large' => $node->activated ? format_date($node->activated, 'large') : t('never'),
-    '%expired_small' => $node->expired ? format_date($node->expired, 'small') : t('never'),
-    '%expired_medium' => $node->expired ? format_date($node->expired, 'medium') : t('never'),
-    '%expired_large' => $node->expired ? format_date($node->expired, 'large') : t('never'),
-    '%autoactivate_small' => $node->autoactivate ? format_date($node->autoactivate, 'small') : t('never'),
-    '%autoactivate_medium' => $node->autoactivate ? format_date($node->autoactivate, 'medium') : t('never'),
-    '%autoactivate_large' => $node->autoactivate ? format_date($node->autoactivate, 'large') : t('never'),
-    '%autoexpire_small' => $node->autoexpire ? format_date($node->autoexpire, 'small') : t('never'),
-    '%autoexpire_medium' => $node->autoexpire ? format_date($node->autoexpire, 'medium') : t('never'),
-    '%autoexpire_large' => $node->autoexpire ? format_date($node->autoexpire, 'large') : t('never'),
-    '%maxviews' => $node->maxviews,
-    '%maxclicks' => $node->maxclicks,
-    '%global_views' => $statistics['global']['views'],
-    '%global_clicks' => $statistics['global']['clicks'],
-    '%last_year_views' => $statistics['last_year']['views'],
-    '%last_year_clicks' => $statistics['last_year']['clicks'],
-    '%this_year_views' => $statistics['this_year']['views'],
-    '%this_year_clicks' => $statistics['this_year']['clicks'],
-    '%last_month_views' => $statistics['last_month']['views'],
-    '%last_month_clicks' => $statistics['last_month']['clicks'],
-    '%this_month_views' => $statistics['this_month']['views'],
-    '%this_month_clicks' => $statistics['this_month']['clicks'],
-    '%this_week_views' => $statistics['this_week']['views'],
-    '%this_week_clicks' => $statistics['this_week']['clicks'],
-    '%yesterday_views' => $statistics['yesterday']['views'],
-    '%yesterday_clicks' => $statistics['yesterday']['clicks'],
-    '%today_views' => $statistics['today']['views'],
-    '%today_clicks' => $statistics['today']['clicks'],
-    '%last_hour_views' => $statistics['last_hour']['views'],
-    '%last_hour_clicks' => $statistics['last_hour']['clicks'],
-    '%this_hour_views' => $statistics['this_hour']['views'],
-    '%this_hour_clicks' => $statistics['this_hour']['clicks'],
-  );
-  // TODO: Add hook to allow other modules to define variables.
+function ad_notify_send_mail($notification, $count = 0, $params = array(), $language = NULL) {
+  $owner = user_load($notification->uid);
 
-  // TODO: Should the from_address be configurable?
-  drupal_mail(
-    'ad_notify_mail',                                   // mail key
-    $notification->address,                             // to address
-    strtr($notification->subject, $variables),          // subject
-    wordwrap(strtr($notification->body, $variables), 72),      // message
-    variable_get('site_mail', ini_get('sendmail_from')) // from address
-  );
+  $send = TRUE;
+  // Send notification only for user who has a permission
+  if (isset($notification->roles) && !empty($notification->roles)) {
+    $send = FALSE;
+    $notification->roles = unserialize($notification->roles);
+    foreach ($notification->roles as $rid) {
+      if (isset($owner->roles[$rid])) {
+        $send = TRUE;
+        break;
+      }
+    }
+  }
+
+  if ($send) {
+    $node = node_load($notification->aid);
+    $node->notification = $notification;
+    $replacements = array(
+      'global' => NULL,
+      'ad' => $node,
+      'ad_owner' => $owner,
+    );
+    $notification->body = token_replace_multiple($notification->body, $replacements, '%', '');
+    $notification->subject = token_replace_multiple($notification->subject, $replacements, '%', '');
+    $params['address'] = $notification->address ? $notification->address : $owner->mail;
+    $params['notification'] = $notification;
+    return drupal_mail('ad_notify', 'notification', $params['address'], $language, $params);
+  }
 }
 
 /**
@@ -217,7 +206,7 @@
       break;
 
     case 'permissions':
-      return array('manage own notifications', 'edit notification email');
+      return array('manage own notifications' => TRUE, 'edit notification email' => TRUE);
       break;
   }
 }
@@ -236,47 +225,101 @@
 }
 
 /**
+ * Implementation of hook_adowners().
+ */
+function ad_notify_adowners($op, $arg1 = NULL, $arg2 = NULL) {
+  switch ($op) {
+    case 'overview':
+      return l(t('notifications'), 'node/'. $arg1 .'/adowners/'. $arg2 .'/notifications');
+
+    case 'add':
+      if ($arg2['aid'] && $arg2['oid']) {
+        $owner = user_load($arg2['uid']);
+
+        // Clone all template notification for new ad owner.
+        $result = db_query('SELECT * FROM {ad_notify} WHERE aid = 0 AND oid = 0');
+        while ($template = db_fetch_object($result)) {
+          db_query("INSERT INTO {ad_notify} (aid, oid, event, delay, expire, locked, status, address, subject, body, roles, template) VALUES(%d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', %d)", $arg2['aid'], $arg2['oid'], $template->event, $template->delay, $template->expire, $template->locked, AD_NOTIFY_ENABLED, $owner->mail, $template->subject, $template->body, $template->roles, $template->notid);
+        }
+      }
+      break;
+
+    case 'remove':
+      if ($arg1) {
+        $result = db_query('DELETE FROM {ad_notify} WHERE oid = %d', $arg1);
+      }
+      break;
+  }
+}
+
+/**
  * Notification overview form.
  */
-function ad_notify_overview_form($form_state, $node, $owner = NULL, $notid = 0) {
+function ad_notify_overview_form($form_state, $node = NULL, $owner = NULL, $notid = 0) {
   global $user;
-  if (arg(2) == 'notifications') {
-    drupal_set_title('My notifications');
+
+  if (isset($owner) && !is_object($owner) && $owner == 0) {
+    $owner = new stdClass();
+    $owner->uid = 0;
   }
-  else {
-    drupal_set_title('Notifications');
-  }
-  if (!isset($owner)) {
+  else if (empty($owner)) {
     $owner = $user;
   }
+  if (!isset($node) || !is_object($node)) {
+    $node = new stdClass();
+    $node->nid = 0;
+  }
 
-  $oid = db_result(db_query('SELECT oid FROM {ad_owners} WHERE aid = %d AND uid = %d', $node->nid, $owner->uid));
-  if (isset($oid)) {
-    $notifications = module_invoke_all('adnotifyapi', 'register');
+  $oid = (int)db_result(db_query('SELECT oid FROM {ad_owners} WHERE aid = %d AND uid = %d', $node->nid, $owner->uid));
+  $notifications = module_invoke_all('adnotifyapi', 'register');
 
-    $header = array(
-      array('data' => t('Last sent'), 'field' => 'sent', 'sort' => 'desc'),
-      array('data' => t('Notification'), 'field' => 'event'),
-      array('data' => t('Status'), 'field' => 'status'),
-      array('data' => t('Action'))
-    );
+  $header = array(
+    array('data' => t('Last sent'), 'field' => 'sent', 'sort' => 'desc'),
+    array('data' => t('Notification'), 'field' => 'event'),
+    array('data' => t('Status'), 'field' => 'status'),
+    array('data' => t('Action'))
+  );
 
-    $sql = "SELECT notid, event, delay, sent, address, status FROM {ad_notify} WHERE oid = %d";
-    $sql .= tablesort_sql($header);
-    $result = pager_query($sql, 25, 0, NULL, $oid);
+  $sql = "SELECT notid, event, delay, sent, address, status, roles FROM {ad_notify} WHERE oid = %d";
+  $sql .= tablesort_sql($header, 'oid DESC, ');
+  $result = pager_query($sql, 25, 0, NULL, $oid);
 
-    $rows = array();
-    while ($notify = db_fetch_object($result)) {
+  $rows = array();
+  while ($notify = db_fetch_object($result)) {
+    $list_notification = TRUE;
+    // Check if user has permission to see the notification.
+    if ($owner->uid && $notify->roles) {
+      $list_notification = FALSE;
+      $notify->roles = unserialize($notify->roles);
+      if (isset($notify->roles) && is_array($notify->roles)) {
+        foreach ($notify->roles as $rid => $require) {
+          if ($require && isset($owner->roles[$rid])) {
+            $list_notification = TRUE;
+            break;
+          }
+        }
+      }
+    }
+
+    if ($list_notification) {
       $row = array();
       $row[] = $notify->sent ? t('!time ago', array('!time' => format_interval(time() - $notify->sent))) : t('Never');
       $row[] = t($notifications[$notify->event], array('@when' => format_interval($notify->delay)));
       $row[] = $notify->status == AD_NOTIFY_ENABLED ? t('enabled') : t('disabled');
-      $row[] = l(t('edit'), 'node/'. $node->nid .'/adowners/'. $owner->uid .'/notifications/' .$notify->notid. '/edit') .' '. l(t('delete'), 'node/'. $node->nid .'/adowners/'. $owner->uid .'/notifications/'. $notify->notid .'/delete');
+      if ($node->nid) {
+        $row[] = l(t('edit'), 'node/'. $node->nid .'/adowners/'. $owner->uid .'/notifications/' .$notify->notid. '/edit') .' '. l(t('delete'), 'node/'. $node->nid .'/adowners/'. $owner->uid .'/notifications/'. $notify->notid .'/delete');
+      }
+      else if (user_access('administer advertisements')) {
+        $row[] = l(t('edit'), 'admin/content/ad/notifications/' .$notify->notid. '/edit') .' '. l(t('delete'), 'admin/content/ad/notifications/'. $notify->notid .'/delete');
+      }
+      else {
+        $row[] = 'N/A';
+      }
       $rows[] = $row;
     }
-    $output = theme('table', $header, $rows);
-    $output .= theme('pager', NULL, 25, 0);
   }
+  $output = theme('table', $header, $rows);
+  $output .= theme('pager', NULL, 25, 0);
 
   $form = array();
 
@@ -284,8 +327,13 @@
     $notification = ad_notification_load($notid);
   }
 
-  $help = '<p>'. t('You can configure one or more notifications for your advertisement using the drop down menus below.  For example, to receive a weekly notification with information about how often your advertisement was viewed and clicked, select the <em>email every @when as long as the ad is active</em> event, and <em>1 week</em> for when.  Or, to receive a reminder that your advertisement will expire in 24 hours select the <em>email @when before the advertisement will expire</em>, and <em>1 day</em> for when.') .'</p>';
-  $help .= '<p>'. t('If you schedule a delay between an event and when you are notified and the event happens multiple times, only one notification will be sent.  For example, if you create a notification for <em>email 1 day after the advertisement is clicked</em> and the ad is clicked 42 more times in the next 24 hours, you will only receive one email 24 hours after your ad was first clicked that notes that your ad was clicked a total of 43 times in the past 24 hours.') .'</p>';
+  if (!isset($owner)) {
+    $help  = '<p>'. t('You can configure one ore more notifications for all advertisements using the drop down menus below.  Ad owners have the ability to manually disable individual global notifications.');
+  }
+  else {
+    $help = '<p>'. t('You can configure one or more notifications for your advertisement using the drop down menus below.  For example, to receive a weekly notification with information about how often your advertisement was viewed and clicked, select the <em>email every @when as long as the ad is active</em> event, and <em>1 week</em> for when.  Or, to receive a reminder that your advertisement will expire in 24 hours select the <em>email @when before the advertisement will expire</em>, and <em>1 day</em> for when.') .'</p>';
+    $help .= '<p>'. t('If you schedule a delay between an event and when you are notified and the event happens multiple times, only one notification will be sent.  For example, if you create a notification for <em>email 1 day after the advertisement is clicked</em> and the ad is clicked 42 more times in the next 24 hours, you will only receive one email 24 hours after your ad was first clicked that notes that your ad was clicked a total of 43 times in the past 24 hours.') .'</p>';
+  }
   $form['create'] = array(
     '#type' => 'fieldset',
     '#description' => $help,
@@ -317,8 +365,7 @@
     '#default_value' => $notid ? $notification->expire : 0,
   );
 
-  if (ad_adaccess($node->nid, 'manage owners') && arg(2) == 'adowners' && 
-      $user->uid != arg(3)) {
+  if (ad_permission($node->nid, 'manage owners') && arg(2) == 'adowners' && $user->uid != arg(3)) {
     $form['create']['locked'] = array(
       '#type' => 'checkbox',
       '#title' => t('Locked'),
@@ -342,20 +389,31 @@
     );
   }
 
-  // TODO: Make it possible for admins to modify email address, and even to
-  // enter multiple email addresses.  Wrap this in a special permission, as 
-  // it involves trust to configure notifications to unverified addresses.
-  $form['create']['mail']['address-display'] = array(
-    '#type' => 'markup',
-    '#value' => '<b>'. t('Notify address') .':</b><br />'. t('The email will be sent to %address.', array('%address' => $owner->mail)),
-    '#prefix' => '<div class="container-inline">',
-    '#suffix' => '</div>',
-  );
+  if (isset($owner->name)) {
+    // TODO: Make it possible for admins to modify email address, and even to
+    // enter multiple email addresses.  Wrap this in a special permission, as
+    // it involves trust to configure notifications to unverified addresses.
+    $form['create']['mail']['address-display'] = array(
+      '#type' => 'markup',
+      '#value' => '<b>'. t('Notify address') .':</b><br />'. t('The email will be sent to %address.', array('%address' => is_object($owner) ? $owner->mail : 0)),
+      '#prefix' => '<div class="container-inline">',
+      '#suffix' => '</div>',
+    );
 
-  $form['create']['mail']['address'] = array(
-    '#type' => 'hidden',
-    '#value' => $owner->mail,
-  );
+    $form['create']['mail']['address'] = array(
+      '#type' => 'hidden',
+      '#value' => is_object($owner) ? $owner->mail : '',
+    );
+  }
+  else {
+    $roles = user_roles(TRUE);
+    $form['create']['roles'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Notify roles'),
+      '#description' => t('Select one or more roles to only send this notification to specific roles. Do not select a role to send notification to all roles.'),
+      '#options' => $roles,
+    );
+  }
 
   if ($notid) {
     $form['create']['mail']['subject'] = array(
@@ -370,32 +428,19 @@
       '#title' => t('Body'),
       '#required' => TRUE,
       '#default_value' => $notification->body,
-      '#description' => t('Enter the body of your notification email.  The following variables can be used in your message and will be automatically replaced before the email is sent:') .'<ul>'
-          .'<li>'. t('%sitename: the name of this website.') 
-          .'<li>'. t('%owner_name: the username of the ad owner.') 
-          .'<li>'. t('%owner_mail: the email address of the ad owner.') 
-          .'<li>'. t('%owner_uid: the user ID of the ad owner.') 
-          .'<li>'. t('%event: the type of event that has triggered this notification.')
-          .'<li>'. t('%count: the number of times the event happened.') 
-          .'<li>'. t('%frequency: a complete sentence describing the frequency this notification will be sent.') 
-          .'<li>'. t('%type: the type of ad.') 
-          .'<li>'. t('%status: the status of the ad.') 
-          .'<li>'. t('%url: the url of the advertisement.') 
-          .'<li>'. t('%siteurl: the url of the website.') 
-          .'<li>'. t('%redirect: the redirection url of the advertisement.') 
-          .'<li>'. t('%title: the title of the advertisement.') 
-          .'<li>'. t('%aid: the ID of the advertisement.') 
-          .'<li>'. t('%comments: the number of comments attached to the advertisement.') 
-          .'<li>'. t('%created_small, %created_medium, %created_large: various formats of when the advertisement was created.') 
-          .'<li>'. t('%activated_small, %activated_medium, %activated_large: various formats of when the advertisement was activated.') 
-          .'<li>'. t('%expired_small, %expired_medium, %expired_large: various formats of when the advertisement was expired.') 
-          .'<li>'. t('%autoactivate_small, %autoactivate_medium, %autoactivate_large: various formats of when the advertisement was automatically activated.') 
-          .'<li>'. t('%autoexpire_small, %autoexpire_medium, %autoexpire_large: various formats of when the advertisement was automatically expired.') 
-          .'<li>'. t('%maxviews: the maximum number of times this advertisement is allowed to be viewed.') 
-          .'<li>'. t('%maxclicks: the maximum number of times this advertisement is allowed to be clicked.') 
-          .'<li>'. t('%global_views, %global_clicks, %last_year_views, %last_year_clicks, %this_year_views, %this_year_clicks, %last_month_views, %last_month_clicks, %this_month_views, %this_month_clicks, %this_week_views, %this_week_clicks, %yesterday_views, %yesterday_clicks, %today_views, %today_clicks, %last_hour_views, %last_hour_clicks, %this_hour_views, %this_hour_clicks: various advertisement statistics')
-          .'</ul>',
     );
+    $form['create']['mail']['tokens'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Replacement patterns'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+    );
+    $form['create']['mail']['tokens']['list'] = array(
+      '#value' => theme('token_help', 'ad', '%', ''),
+    );
+    if ($notification->roles) {
+      $form['create']['roles']['#default_value'] = $notification->roles;
+    }
   }
 
   $form['create']['oid'] = array(
@@ -446,10 +491,17 @@
   else {
     $form['notifications'] = array(
       '#type' => 'markup',
-      '#value' => '<p>'. t('There are no notifications configured for %owner.', array('%owner' => $owner->name)) .'</p>',
+      '#value' => '<p>'. t('There are no notifications configured yet.') .'</p>',
     );
   }
 
+  if ($node->nid && isset($owner) && is_object($owner) && isset($owner->name) && $user->name == $owner->name) {
+    drupal_set_title('My notifications');
+  }
+  else {
+    drupal_set_title('Notifications');
+  }
+
   return $form;
 }
 
@@ -457,29 +509,16 @@
  * Validate ad notifications before saving to database.
  */
 function ad_notify_overview_form_validate($form, &$form_state) {
-  $redirect = FALSE;
   if ($form_state['values']['event'] == 'regular' && $form_state['values']['delay'] < 3600) {
-    drupal_set_message(t('You are not allowed to schedule a regular notification more frequently than once an hour.'), 'error');
-    $redirect = TRUE;
+    form_set_error('form', t('You are not allowed to schedule a regular notification more frequently than once an hour.'));
   }
   else if (!isset($form_state['values']['notid'])) {
     if (db_result(db_query("SELECT notid FROM {ad_notify} WHERE oid = %d AND event = '%s' AND delay = %d", $form_state['values']['oid'], $form_state['values']['event'], $form_state['values']['delay']))) {
-      drupal_set_message(t('You have already scheduled that notification.'), 'error');
-      $redirect = TRUE;
+      form_set_error('form', t('You have already scheduled that notification.'));
     }
   }
-  else if ($form_state['values']['locked'] && !ad_adaccess($form_state['values']['aid'], 'manage owners')) {
-    $redirect = TRUE;
-    drupal_set_message(t('This notification is locked, you will need to contact the site administrator to edit this notification for you.'), 'error');
-  }
-
-  if ($redirect) {
-    if (arg(2) == 'adowners' && arg(4) == 'notifications') {
-      drupal_goto('node/'. $form_state['values']['aid'] .'/adowners/'. $form_state['values']['uid'] .'/notifications');
-    }
-    else {
-      drupal_goto('node/'. $form_state['values']['aid'] .'/notifications');
-    }
+  else if ($form_state['values']['locked'] && !ad_permission($form_state['values']['aid'], 'manage owners')) {
+    form_set_error('form', t('This notification is locked, you will need to contact the site administrator to edit this notification for you.'));
   }
 }
 
@@ -487,8 +526,22 @@
  * Save notifications to database.
  */
 function ad_notify_overview_form_submit($form, &$form_state) {
+  global $user;
+
+  // Clean up roles value before saving to DB
+  if (isset($form_state['values']['roles'])) {
+    foreach ($form_state['values']['roles'] as $rid => $require) {
+      if (!$require) {
+        unset($form_state['values']['roles'][$rid]);
+      }
+    }
+    if (empty($form_state['values']['roles'])) {
+      $form_state['values']['roles'] = NULL;
+    }
+  }
+
   if (isset($form_state['values']['notid'])) {
-    db_query("UPDATE {ad_notify} SET aid = %d, oid = %d, event = '%s', delay = %d, expire = %d, locked = %d, status = %d, address = '%s', subject = '%s', body = '%s' WHERE notid = %d", $form_state['values']['aid'], $form_state['values']['oid'], $form_state['values']['event'], $form_state['values']['delay'], $form_state['values']['expire'], $form_state['values']['locked'], AD_NOTIFY_ENABLED, $form_state['values']['address'], $form_state['values']['subject'], $form_state['values']['body'], $form_state['values']['notid']);
+    db_query("UPDATE {ad_notify} SET aid = %d, oid = %d, event = '%s', delay = %d, expire = %d, locked = %d, status = %d, address = '%s', subject = '%s', body = '%s', roles = '%s' WHERE notid = %d", $form_state['values']['aid'], $form_state['values']['oid'], $form_state['values']['event'], $form_state['values']['delay'], $form_state['values']['expire'], $form_state['values']['locked'], AD_NOTIFY_ENABLED, isset($form_state['values']['address']) ? $form_state['values']['address'] : '', $form_state['values']['subject'], $form_state['values']['body'], isset($form_state['values']['roles']) ? serialize($form_state['values']['roles']) : '', $form_state['values']['notid']);
     drupal_set_message('Notification updated.');
   }
   else {
@@ -497,34 +550,50 @@
     if ($mail == array()) {
       // Default message text.
       $mail = array(
-        'subject' => t('[%sitename ad] %event notification'),
-        'body' => t("Hello %owner_name,\n\n  This is an automatically generated notification about your advertisement \"%title\" that is being displayed on the %sitename website.\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 about your advertisement \"%title\" that is being displayed on the %site-name website.\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 %site-name Team\n\n-\n%site-url"),
       );
     }
-    db_query("INSERT INTO {ad_notify} (aid, oid, event, delay, expire, locked, status, address, subject, body) VALUES(%d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s')", $form_state['values']['aid'], $form_state['values']['oid'], $form_state['values']['event'], $form_state['values']['delay'], $form_state['values']['expire'], $form_state['values']['locked'], AD_NOTIFY_ENABLED, $form_state['values']['address'], $mail['subject'], $mail['body']);
+    db_query("INSERT INTO {ad_notify} (aid, oid, event, delay, expire, locked, status, address, subject, body, roles) VALUES(%d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s')", $form_state['values']['aid'], $form_state['values']['oid'], $form_state['values']['event'], $form_state['values']['delay'], $form_state['values']['expire'], $form_state['values']['locked'], AD_NOTIFY_ENABLED, isset($form_state['values']['address']) ? $form_state['values']['address'] : '', $mail['subject'], $mail['body'], isset($form_state['values']['roles']) ? serialize($form_state['values']['roles']) : '');
+
+    // Clone new template notification for all ad owners.
+    if (!$form_state['values']['oid']) {
+      $template_id = db_last_insert_id('ad_notify', 'notid');
+
+      $result = db_query('SELECT ao.aid, ao.oid, u.mail FROM {ad_owners} ao LEFT JOIN {users} u ON ao.uid = u.uid');
+      while ($owner = db_fetch_object($result)) {
+        // This INSERT can throw "duplicate key" errors in some situations, so just running it silently
+        @db_query("INSERT INTO {ad_notify} (aid, oid, event, delay, expire, locked, status, address, subject, body, roles, template) VALUES(%d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', %d)", $owner->aid, $owner->oid, $form_state['values']['event'], $form_state['values']['delay'], $form_state['values']['expire'], $form_state['values']['locked'], AD_NOTIFY_ENABLED, $owner->mail, $mail['subject'], $mail['body'], isset($form_state['values']['roles']) ? serialize($form_state['values']['roles']) : '', $template_id);
+      }
+    }
     drupal_set_message('Notification created.');
   }
-  $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/adowners/'. $form_state['values']['uid'] .'/notifications';
+
+  if ($form_state['values']['aid'] && $form_state['values']['uid'] && $user->uid != $form_state['values']['uid']) {
+    $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/adowners/'. $form_state['values']['uid'] .'/notifications';
+  }
+  else if ($form_state['values']['aid']) {
+    $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/notifications';
+  }
+  else {
+    $form_state['redirect'] = 'admin/content/ad/notifications';
+  }
 }
 
 /**
  * Load a specified notification from the database, return as an object.
  */
 function ad_notification_load($notid) {
-  return db_fetch_object(db_query('SELECT * FROM {ad_notify} WHERE notid = %d', $notid));
-}
-
-/**
- * Display confirm form.
- */
-function ad_notify_confirm_delete_page($node, $owner, $notification) {
-  return drupal_get_form('ad_notify_confirm_delete', $node, $owner, $notification);
+  $notification = db_fetch_object(db_query('SELECT * FROM {ad_notify} WHERE notid = %d', $notid));
+  $notification->roles = unserialize($notification->roles);
+  return $notification;
 }
 
 /**
  * Confirm deletion of a specified notification from the database.
  */
 function ad_notify_confirm_delete(&$form_state, $node, $owner, $notification) {
+  global $user;
   $form = array();
 
   $form['oid'] = array(
@@ -534,12 +603,12 @@
 
   $form['aid'] = array(
     '#type' => 'hidden',
-    '#value' => $node->nid,
+    '#value' => isset($node->nid) ? $node->nid : 0,
   );
 
   $form['uid'] = array(
     '#type' => 'hidden',
-    '#value' => $owner->uid,
+    '#value' => is_object($owner) ? $owner->uid : 0,
   );
 
   $form['notid'] = array(
@@ -564,10 +633,34 @@
     '#suffix' => '</div>',
   );
 
+  if (!$notification->oid) {
+    $linked = db_result(db_query('SELECT COUNT(*) FROM {ad_notify} WHERE template = %d', $notification->notid));
+    if ($linked) {
+      $form['remove_linked'] = array(
+        '#type' => 'radios',
+        '#options' => array(
+          1 => t('Completely remove this notification.'),
+          0 => t('Remove from further usage, but leave existing notifications.'),
+        ),
+        '#default_value' => 1,
+      );
+    }
+  }
+
+  if (isset($node->nid) && $owner->uid && $user->uid != $owner->uid) {
+    $path = 'node/'. $node->nid .'/adowners/'. $owner->uid .'/notifications';
+  }
+  else if (isset($node->nid)) {
+    $path = 'node/'. $node->nid .'/notifications';
+  }
+  else {
+    $path = 'admin/content/ad/notifications';
+  }
+
   $form = confirm_form(
     $form,
     'Are you sure you want to delete this notification?',
-    'node/'. $node->nid .'/adowners/'. $owner->uid .'/notifications',
+    $path,
     'This action cannot be undone.',
     'Delete',
     'Cancel'
@@ -580,14 +673,8 @@
  * Validate that the selected notification can be deleted.
  */
 function ad_notify_confirm_delete_validate($form, &$form_state) {
-  if ($form_state['values']['locked'] && !ad_adaccess($form_state['values']['aid'], 'manage owners')) {
-    drupal_set_message(t('This notification is locked, you will need to contact the site administrator to delete this notification for you.'), 'error');
-    if (arg(2) == 'adowners' && arg(4) == 'notifications') {
-      drupal_goto('node/'. $form_state['values']['aid'] .'/adowners/'. $form_state['values']['uid'] .'/notifications');
-    }
-    else {
-      drupal_goto('node/'. $form_state['values']['aid'] .'/notifications');
-    }
+  if ($form_state['values']['locked'] && !ad_permission($form_state['values']['aid'], 'manage owners')) {
+    form_set_error('form', t('This notification is locked, you will need to contact the site administrator to delete this notification for you.'));
   }
 }
 
@@ -595,23 +682,24 @@
  * Delete a specified notification from the database.
  */
 function ad_notify_confirm_delete_submit($form, &$form_state) {
+  global $user;
   db_query('DELETE FROM {ad_notify} WHERE notid = %d', $form_state['values']['notid']);
-  drupal_set_message('Notification deleted.');
-  $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/adowners/'. $form_state['values']['uid'] .'/notifications';
-}
 
-/**
- * Implementation of hook_adowners().
- */
-function ad_notify_adowners($op, $arg1 = NULL, $arg2 = NULL) {
-  switch ($op) {
-    case 'overview':
-      return l(t('notifications'), 'node/'. $arg1 .'/adowners/'. $arg2 .'/notifications');
+  if (isset($form_state['values']['remove_linked']) && $form_state['values']['remove_linked']) {
+    db_query('DELETE FROM {ad_notify} WHERE template = %d', $form_state['values']['notid']);
+    drupal_set_message('Template and all derived notifications were deleted.');
+  }
+  else {
+    drupal_set_message('Notification deleted.');
+  }
 
-    case 'delete':
-      if ($arg1) {
-        db_query('DELETE FROM {ad_notify} WHERE oid = %d', $arg1);
-      }
-      break;
+  if ($form_state['values']['aid'] && $form_state['values']['uid'] && $user->uid != $form_state['values']['uid']) {
+    $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/adowners/'. $form_state['values']['uid'] .'/notifications';
+  }
+  else if ($form_state['values']['aid']) {
+    $form_state['redirect'] = 'node/'. $form_state['values']['aid'] .'/notifications';
+  }
+  else {
+    $form_state['redirect'] = 'admin/content/ad/notifications';
   }
 }