diff --git a/includes/admin.settings.inc b/includes/admin.settings.inc
index c65c20c..90a6d73 100644
--- a/includes/admin.settings.inc
+++ b/includes/admin.settings.inc
@@ -26,6 +26,21 @@ function project_issue_settings_form(&$form_state) {
     '#description' => t('If selected, user signatures will be appended to the display of issue followups.'),
   );
 
+  if (module_exists('flag')) {
+    $flags = flag_get_flags();
+    $flag_options[0] = t('- None -');
+    foreach ($flags as $flag) {
+      $flag_options[$flag->name] = $flag->title;
+    }
+    $form['project_issue_subscription_flag'] = array(
+      '#title' => t('Issue subscription flag'),
+      '#type' => 'select',
+      '#options' => $flag_options,
+      '#default_value' => variable_get('project_issue_subscription_flag', NULL),
+      '#description' => t('Allows users to receive e-mail notifications about issues that have been flagged with the selected flag. When none is selected, users can only subscribe by commenting on an issue.'),
+    );
+  }
+
   $form['project_issue_reply_to'] = array(
     '#type' => 'textfield',
     '#title' => t('Reply-to address on e-mail notifications'),
diff --git a/includes/mail.inc b/includes/mail.inc
index 0e510fc..7efe394 100644
--- a/includes/mail.inc
+++ b/includes/mail.inc
@@ -249,15 +249,101 @@ function project_issue_mail_notify($nid) {
     }
   }
 
-  if (count($uids)) {
+  // Retrieve list of users being globally subscribed to all issues.
+  $accounts_all_issues = array();
+  $result = db_query("SELECT pisu.uid, u.name, u.mail FROM {project_issue_subscriptions_user} pisu INNER JOIN {users} u ON pisu.uid = u.uid WHERE u.status = 1 AND pisu.level = " . PROJECT_ISSUE_SUBSCRIPTIONS_ALL);
+  while ($account = db_fetch_object($result)) {
+    $accounts_all_issues[$account->uid] = $account;
+  }
+
+  // Check whether Flag module integration is enabled for e-mail notifications.
+  $flag_integration = (module_exists('flag') && variable_get('project_issue_subscription_flag', 0));
+
+  // Retrieve list of users being globally subscribed to "own" issues (without
+  // Flag integration).
+  $accounts_flagged_issues = array();
+  if (!$flag_integration) {
+    $result = db_query("SELECT pisu.uid, u.name, u.mail FROM {project_issue_subscriptions_user} pisu INNER JOIN {users} u ON pisu.uid = u.uid WHERE u.status = 1 AND pisu.level = " . PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED);
+    while ($account = db_fetch_object($result)) {
+      $accounts_flagged_issues[$account->uid] = $account;
+    }
+  }
+
+  // Check per-project subscriptions of involved users.
+  if (!empty($uids)) {
     $placeholders = implode(',', array_fill(0, count($uids), '%d'));
-    array_unshift($uids, $node->project_issue['pid']);
-    $result = db_query("SELECT p.*, u.uid, u.name, u.mail FROM {project_subscriptions} p INNER JOIN {users} u ON p.uid = u.uid WHERE u.status = 1 AND p.nid = %d AND (p.level = 2 OR (p.level = 1 AND u.uid IN ($placeholders)))", $uids);
+    $args = $uids;
+    array_unshift($args, $node->project_issue['pid']);
+
+    // Check which involved users are subscribed to all issues of the project.
+    $result = db_query("SELECT pisp.uid, u.name, u.mail
+      FROM {project_issue_subscriptions_project} pisp
+      INNER JOIN {users} u ON pisp.uid = u.uid
+      WHERE u.status = 1 AND pisp.nid = %d AND pisp.uid IN ($placeholders) AND pisp.level = " . PROJECT_ISSUE_SUBSCRIPTIONS_ALL, $args);
+    while ($account = db_fetch_object($result)) {
+      $accounts_all_issues[$account->uid] = $account;
+    }
+
+    // Check which involved users are subscribed to "own" issues of the project
+    // (without Flag integration).
+    if (!$flag_integration) {
+      $result = db_query("SELECT pisp.uid, u.name, u.mail
+        FROM {project_issue_subscriptions_project} pisp
+        INNER JOIN {users} u ON pisp.uid = u.uid
+        WHERE u.status = 1 AND pisp.nid = %d AND pisp.uid IN ($placeholders) AND pisp.level = " . PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED, $args);
+      while ($account = db_fetch_object($result)) {
+        $accounts_flagged_issues[$account->uid] = $account;
+      }
+    }
   }
-  else {
-    $result = db_query('SELECT p.*, u.uid, u.name, u.mail FROM {project_subscriptions} p INNER JOIN {users} u ON p.uid = u.uid WHERE u.status = 1 AND p.nid = %d AND p.level = 2', $node->project_issue['pid']);
+
+  // Check which users subscribed to the issue via Flag.
+  if ($flag_integration) {
+    // Retrieve all users who flagged the issue.
+    $flag_contents = flag_get_content_flags('node', $node->nid, variable_get('project_issue_subscription_flag', 0));
+
+    // Now use the list of users that flagged the issue to retrieve the list of
+    // users who are subscribed to flagged issues of the project. For example, a
+    // user who globally subscribed to no issues should not get an e-mail
+    // notification after flagging the issue. That should only happen when the
+    // user subscribed to flagged issues for the project.
+    if (!empty($flag_contents)) {
+      $flags_uids = array_keys($flag_contents);
+      $placeholders = implode(',', array_fill(0, count($flags_uids), '%d'));
+      $args = $flags_uids;
+
+      // Check which users flagged the issue and globally subscribed to flagged
+      // issues (across projects).
+      $result = db_query("SELECT pisu.uid, u.name, u.mail
+        FROM {project_issue_subscriptions_user} pisu
+        INNER JOIN {users} u ON pisu.uid = u.uid
+        WHERE u.status = 1 AND pisu.uid IN ($placeholders) AND pisu.level = " . PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED, $args);
+      while ($account = db_fetch_object($result)) {
+        $accounts_flagged_issues[$account->uid] = $account;
+      }
+
+      // Check which users flagged the issue and subscribed to flagged issues of
+      // the project.
+      array_unshift($args, $node->project_issue['pid']);
+      $result = db_query("SELECT pisp.uid, u.name, u.mail
+        FROM {project_issue_subscriptions_project} pisp
+        INNER JOIN {users} u ON pisp.uid = u.uid
+        WHERE u.status = 1 AND pisp.nid = %d AND pisp.uid IN ($placeholders) AND pisp.level = " . PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED, $args);
+      while ($account = db_fetch_object($result)) {
+        $accounts_flagged_issues[$account->uid] = $account;
+      }
+    }
   }
 
+  // Lastly, join the lists of users being
+  // - globally subscribed to all issues
+  // - globally subscribed to own issues (without Flag integration)
+  // - globally subscribed to flagged issues
+  // - subscribed to all issues of the project
+  // - subscribed to own issues of the project (without Flag integration)
+  // - subscribed to flagged issues of the project
+  $recipients = $accounts_all_issues + $accounts_flagged_issues;
+
   // To save workload, check here if either the anonymous role or the
   // authenticated role has the 'view uploaded files' permission, since
   // we only need to process each user's file access permission if this
@@ -289,7 +375,7 @@ function project_issue_mail_notify($nid) {
   // some punctuation) are used.  See example in Appendix A.1.2.
   $from = '"' . mime_header_encode($sender->name) . "\" <$sender->mail>";
 
-  while ($recipient = db_fetch_object($result)) {
+  foreach ($recipients as $recipient) {
     // To save work, only go through a user_load if we need it.
     if ($check_file_perms || $check_node_access) {
       $account = user_load(array('uid' => $recipient->uid));
diff --git a/includes/project_node.inc b/includes/project_node.inc
index ce6c661..f62e18a 100644
--- a/includes/project_node.inc
+++ b/includes/project_node.inc
@@ -65,6 +65,6 @@ function project_issue_project_delete($node) {
     node_delete($issue->nid);
   }
   db_query('DELETE FROM {project_issue_projects} WHERE nid = %d', $node->nid);
-  db_query('DELETE FROM {project_subscriptions} WHERE nid = %d', $node->nid);
+  db_query('DELETE FROM {project_issue_subscriptions_project} WHERE nid = %d', $node->nid);
 }
 
diff --git a/includes/subscribe.inc b/includes/subscribe.inc
index 703114b..a4324a9 100644
--- a/includes/subscribe.inc
+++ b/includes/subscribe.inc
@@ -1,172 +1,182 @@
 <?php
 
-function project_issue_subscribe($form_state, $project_nid = 0) {
-  global $user;
+/**
+ * Returns project issue subscription levels.
+ */
+function _project_issue_subscription_levels() {
+  return array(
+    PROJECT_ISSUE_SUBSCRIPTIONS_NONE => t('None'),
+    PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED => t('Subscribed issues'),
+    PROJECT_ISSUE_SUBSCRIPTIONS_ALL => t('All issues'),
+  );
+}
 
-  if (!valid_email_address($user->mail)) {
-    drupal_set_message(t('You need to provide a valid e-mail address to subscribe to issue e-mails. Please edit your user information.'), 'error');
-    drupal_goto('user/'. $user->uid .'/edit');
-  }
+/**
+ * Form constructor for global user project issue subscriptions.
+ */
+function project_issue_subscriptions_user_form(&$form_state, $account) {
+  // Global subscription level.
+  project_issue_subscriptions_user_settings_load($account);
 
-  $levels = array(0 => t('None'), 1 => t('Own issues'), 2 => t('All issues'));
+  $form['account'] = array(
+    '#type' => 'value',
+    '#value' => $account,
+  );
+  $form['#tree'] = TRUE;
 
-  if ($project_nid) {
-    if (!is_numeric($project_nid)) {
-      $project_nid = db_result(db_query(db_rewrite_sql("SELECT p.nid FROM {project_projects} p WHERE p.uri = '%s'", 'p'), $project_nid));
-    }
-    if (!$project_nid) {
-      return drupal_not_found();
-    }
+  $form['project_issue_subscriptions'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Issue subscriptions'),
+    '#collapsible' => TRUE,
+  );
 
-    $project = node_load($project_nid);
-    project_project_set_breadcrumb($project, TRUE);
+  $form['project_issue_subscriptions']['level'] = array(
+    '#type' => 'radios',
+    '#title' => t('Global project issue subscription level'),
+    '#options' => _project_issue_subscription_levels(),
+    '#default_value' => $account->project_issue_subscriptions['level'],
+  );
 
-    $level = db_result(db_query('SELECT level FROM {project_subscriptions} WHERE nid = %d AND uid = %d', $project->nid, $user->uid));
-    $form['single'] = array(
-      '#type' => 'value',
-      '#value' => $project->nid,
-    );
-    $form['#project'] = array(
-      '#type' => 'value',
-      '#value' => $project,
-    );
-    $form['subscribe'] = array(
-      '#type' => 'markup',
-      '#value' => '<p>'. t('Subscribe to receive e-mail notification when an issue for this project is updated.') .'</p>',
+  // Per-project subscription level (only enabled).
+  // We only allow to change (and remove) per-project subscriptions on this
+  // form. Users are able to subscribe to further projects by visiting the
+  // individual project pages. In terms of UX, that's preferred anyway, since
+  // a user normally wants to know and be sure what exactly she subscribes to.
+  $form['project_issue_subscriptions']['projects'] = array(
+    '#theme' => 'project_issue_subscriptions_projects_table',
+    '#header' => array(t('Project'), t('Subscription level')),
+    '#description' => t('All project subscription levels with the same or lower level as your global default level will be removed.'),
+  );
+  $result = db_query(db_rewrite_sql("SELECT pisp.nid, n.title, pisp.level
+    FROM {project_issue_subscriptions_project} pisp
+    INNER JOIN {node} n ON n.nid = pisp.nid
+    WHERE n.type = 'project_project' AND n.status = 1 AND pisp.uid = %d ORDER BY n.title
+    ", 'n'), $account->uid);
+  while ($project = db_fetch_object($result)) {
+    $form['project_issue_subscriptions']['projects'][$project->nid]['title'] = array(
+      '#value' => l($project->title, "node/$project->nid"),
     );
-    $form['options']['#tree'] = TRUE;
-    $form['options'][$project->nid] = array(
-      '#type' => 'radios',
-      '#title' => t('Subscribe to @project issues', array('@project' => $project->title)),
-      '#default_value' => isset($level) ? $level : 0,
-      '#options' => $levels,
+    $form['project_issue_subscriptions']['projects'][$project->nid]['level'] = array(
+      '#type' => 'select',
+      '#options' => $form['project_issue_subscriptions']['level']['#options'],
     );
-
   }
-  else {
 
-    $form['buttons']['all'] = array(
-      '#type' => 'markup',
-      '#value' => t('All projects'),
-    );
-    foreach ($levels as $key => $level) {
-      $form['buttons'][$level] = array(
-        '#type' => 'submit',
-        '#name' => 'all',
-        '#value' => $level,
-      );
-    }
-    $nids = array();
-
-    $result = db_query(db_rewrite_sql("SELECT s.nid, n.title, s.level, p.uri FROM {project_subscriptions} s INNER JOIN {node} n ON n.nid = s.nid INNER JOIN {project_projects} p ON n.nid = p.nid WHERE n.type = 'project_project' AND n.status = 1 AND s.uid = %d ORDER BY n.title", 's'), $user->uid);
-    while ($project = db_fetch_object($result)) {
-      $form['project'][$project->nid]['title'] = array(
-        '#value' => l($project->title, "node/$project->nid"),
-      );
-      foreach ($levels as $key => $level) {
-        if ($project->level == $key) {
-          $status[$project->nid] = $key;
-        }
-      }
-      $nids[] = $project->nid;
-    }
-
-    if (empty($nids)) {
-      $placeholders = '';
-    }
-    else {
-      $placeholders = " AND n.nid NOT IN (". implode(',', array_fill(0, count($nids), '%d')) .")";
-    }
-
-    $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, p.uri FROM {node} n INNER JOIN {project_projects} p ON n.nid = p.nid WHERE n.type = 'project_project' AND n.status = 1". ($nids ?  $placeholders : "") ." ORDER BY n.title"), $nids);
-    while ($project = db_fetch_object($result)) {
-      $form['project'][$project->nid]['title'] = array(
-        '#value' => l($project->title, "node/$project->nid"),
-      );
-      $nids[] = $project->nid;
-    }
-
-    foreach ($nids as $nid) {
-      $form['options']['#tree'] = TRUE;
-      $form['options'][$nid] = array(
-        '#type' => 'radios',
-        '#default_value' => isset($status[$nid]) ? $status[$nid] : 0,
-        '#options' => $levels,
-      );
-    }
-  }
-  $form['submit'] = array(
+  $form['actions']['submit'] = array(
     '#type' => 'submit',
-    '#value' => t('Subscribe'),
+    '#value' => t('Save'),
+    '#weight' => 100,
   );
   return $form;
 }
 
-function theme_project_issue_subscribe($form) {
-  global $user;
-
-  $output = '';
-
-  if (empty($form['#project'])) {
-    $output .= project_issue_query_result_links();
-  }
-  else {
-    $project = $form['#project']['#value'];
-    $output .= project_issue_query_result_links($project->project['uri']);
+/**
+ * Form submission handler for project_issue_user_subscribe_form().
+ */
+function project_issue_subscriptions_user_form_submit($form, &$form_state) {
+  // Update the global issue subscription settings.
+  $account = $form_state['values']['account'];
+  $account->project_issue_subscriptions = $form_state['values']['project_issue_subscriptions'];
+  project_issue_subscriptions_user_settings_save($account);
+
+  // Delete the existing per-project settings.
+  db_query("DELETE FROM {project_issue_subscriptions_project} WHERE uid = %d", array($account->uid));
+
+  // Insert the new per-project settings.
+  foreach ($form_state['values']['project_issue_subscriptions']['projects'] as $nid => $level) {
+    // Skip all per-project settings having the same or a lower level than the
+    // global default.
+    if ($level > $form_state['values']['project_issue_subscriptions']['level']) {
+      db_query("INSERT INTO {project_issue_subscriptions_project} (uid, nid, level) VALUES (%d, %d, %d)", array($account->uid, $nid, $level));
+    }
   }
 
-  if (!isset($form['single'])) {
-    $levels = array(0 => t('None'), 1 => t('Own issues'), 2 => t('All issues'));
-    $headers = array_merge(array(t('Project')), $levels);
+  drupal_set_message(t('Your subscription settings have been updated.'));
+}
 
+/**
+ * Returns HTML for per-project subscription levels table in project_issue_user_subscribe_form().
+ */
+function theme_project_issue_subscriptions_projects_table($element) {
+  $output = '';
+  $rows = array();
+  foreach (element_children($element) as $nid) {
     $row = array();
-    foreach (element_children($form['buttons']) as $key) {
-      $row[] = drupal_render($form['buttons'][$key]);
-    }
-    $rows = array($row);
-
-    foreach (element_children($form['project']) as $key) {
-      $row = array(drupal_render($form['project'][$key]['title']));
-      foreach ($levels as $level => $name) {
-        $row[] = drupal_render($form['options'][$key][$level]);
-      }
-      $rows[] = $row;
-    }
-    $output .= theme('table', $headers, $rows);
+    $row[] = drupal_render($element[$nid]['title']);
+    $row[] = drupal_render($element[$nid]['level']);
+    $rows[] = array();
+  }
+  if (!empty($rows)) {
+    $output .= theme('table', $element['#header'], $rows);
+    $output .= check_plain($element['#description']);
   }
 
-  $output .= drupal_render($form);
+  $output .= drupal_render($element);
   return $output;
 }
 
-function project_issue_subscribe_submit($form, &$form_state) {
-
+/**
+ * Form constructor for per-project issue subscription.
+ */
+function project_issue_subscriptions_project_form($form_state, $project_nid) {
   global $user;
-  $all = $form_state['clicked_button']['#value'];
 
-  $levels = array(0 => t('None'), 1 => t('Own issues'), 2 => t('All issues'));
+  // @todo Isn't this globally enforced elsewhere...?
+  if (!valid_email_address($user->mail)) {
+    drupal_set_message(t('You need to provide a valid e-mail address to subscribe to issue e-mails. Please edit your user information.'), 'error');
+    drupal_goto('user/'. $user->uid .'/edit');
+  }
 
-    // Remove previous subscriptions for user.
-    if (isset($form_state['values']['single'])) {
-      db_query('DELETE FROM {project_subscriptions} WHERE nid = %d AND uid = %d', $form_state['values']['single'], $user->uid);
-    }
-    else {
-      db_query('DELETE FROM {project_subscriptions} WHERE uid = %d', $user->uid);
-    }
+  if (!is_numeric($project_nid)) {
+    $project_nid = project_get_nid_from_uri($project_nid);
+  }
+  if (!$project_nid || !($project = node_load($project_nid))) {
+    return drupal_not_found();
+  }
 
-    $_level = array_search($all, $levels);
+  project_project_set_breadcrumb($project, TRUE);
+  drupal_set_title(t('Subscribe to @project issues', array('@project' => $project->title)));
 
-    foreach ($form_state['values']['options'] as $nid => $level) {
-      if ($_level !== 0 && $level !== 0) {
-        db_query('INSERT INTO {project_subscriptions} (nid, uid, level) VALUES (%d, %d, %d)', $nid, $user->uid, $_level ? $_level : $level);
-      }
-    }
-    drupal_set_message(t('Subscription settings saved.'));
+  $form['nid'] = array(
+    '#type' => 'value',
+    '#value' => $project->nid,
+  );
+  $form['uid'] = array(
+    '#type' => 'value',
+    '#value' => $user->uid,
+  );
 
-    if (isset($form['single'])) {
-      $form_state['redirect'] = 'project/issues/subscribe-mail/'. $form['#project']['#value']->project['uri'];
-    }
-    else {
-      $form_state['redirect'] = 'project/issues/subscribe-mail';
-    }
+  $level = db_result(db_query('SELECT level FROM {project_issue_subscriptions_project} WHERE nid = %d AND uid = %d', $project->nid, $user->uid));
+  // Default to None.
+  if (!$level) {
+    $level = PROJECT_ISSUE_SUBSCRIPTIONS_NONE;
+  }
+  $form['level'] = array(
+    '#type' => 'radios',
+    '#title' => t('Subscription level'),
+    '#options' => _project_issue_subscription_levels(),
+    '#default_value' => $level,
+    '#description' => t('Receive an e-mail notification when an issue for this project is updated.'),
+  );
+
+  $form['actions']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Subscribe'),
+  );
+  return $form;
+}
+
+/**
+ * Form submission handler for project_issue_user_subscribe_form().
+ */
+function project_issue_subscriptions_project_form_submit($form, &$form_state) {
+  // Delete the existing per-project setting.
+  db_query("DELETE FROM {project_issue_subscriptions_project} WHERE nid = %d AND uid = %d", array($form_state['values']['nid'], $form_state['values']['uid']));
+
+  // Insert the new per-project setting.
+  if ($form_state['values']['level'] > PROJECT_ISSUE_SUBSCRIPTIONS_NONE) {
+    db_query("INSERT INTO {project_issue_subscriptions_project} (nid, uid, level) VALUES (%d, %d, %d)", array($form_state['values']['nid'], $form_state['values']['uid'], $form_state['values']['level']));
+  }
+
+  drupal_set_message(t('Your subscription setting has been updated.'));
 }
diff --git a/project_issue.info b/project_issue.info
index 2225d1a..a70606e 100644
--- a/project_issue.info
+++ b/project_issue.info
@@ -1,9 +1,10 @@
 name = Project issue tracking
 description = Provides issue tracking for the project.module.
+package = Project
+core = 6.x
 dependencies[] = project
 dependencies[] = views
 dependencies[] = comment
 dependencies[] = comment_upload
 dependencies[] = upload
-package = Project
-core = 6.x
+recommends[] = flag
diff --git a/project_issue.install b/project_issue.install
index 1c24aff..1a3ff48 100644
--- a/project_issue.install
+++ b/project_issue.install
@@ -282,7 +282,29 @@ function project_issue_schema() {
     'primary key' => array('priority'),
   );
 
-  $schema['project_subscriptions'] = array(
+  $schema['project_issue_subscriptions_user'] = array(
+    'description' => 'Stores global issue subscriptions per user.',
+    'fields' => array(
+      'uid' => array(
+        'description' => 'The {users}.uid for this subscriber.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'level' => array(
+        'description' => 'The global subscription setting level. 0 = None, 1 = Flagged, 2 = All.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('uid', 'level'),
+  );
+
+  $schema['project_issue_subscriptions_project'] = array(
     'description' => 'Table keeping track of per-user project_issue subscriptions.',
     'fields' => array(
       'nid' => array(
@@ -626,3 +648,44 @@ function project_issue_update_6006() {
 
   return $ret;
 }
+
+/**
+ * Revamp project issue subscriptions for Flag integration.
+ */
+function project_issue_update_6007() {
+  $ret = array();
+
+  // Delete obsolete 'project_issue_global_subscribe_page' variable.
+  variable_del('project_issue_global_subscribe_page');
+
+  // Rename {project_subscriptions} to {project_issue_subscriptions_project}.
+  db_rename_table($ret, 'project_subscriptions', 'project_issue_subscriptions_project');
+
+  // Ensure that existing project subscriptions are clean.
+  $ret[] = update_sql("DELETE FROM {project_issue_subscriptions_project} WHERE level = 0");
+
+  // Create new global issue subscription setting table.
+  db_create_table($ret, 'project_issue_subscriptions_user', array(
+    'description' => 'Stores global issue subscriptions per user.',
+    'fields' => array(
+      'uid' => array(
+        'description' => 'The {users}.uid for this subscriber.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'level' => array(
+        'description' => 'The global subscription setting level. 0 = None, 1 = Flagged, 2 = All.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('uid', 'level'),
+  ));
+
+  return $ret;
+}
diff --git a/project_issue.module b/project_issue.module
index 4345d1d..1d4f250 100644
--- a/project_issue.module
+++ b/project_issue.module
@@ -11,6 +11,21 @@ define('PROJECT_ISSUE_STATE_FIXED', 2);
 define('PROJECT_ISSUE_STATE_CLOSED', 7);
 
 /**
+ * Project issue subscriptions level: Not subscribed.
+ */
+define('PROJECT_ISSUE_SUBSCRIPTIONS_NONE', 0);
+
+/**
+ * Project issue subscriptions level: Own/flagged issues.
+ */
+define('PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED', 1);
+
+/**
+ * Project issue subscriptions level: All issues.
+ */
+define('PROJECT_ISSUE_SUBSCRIPTIONS_ALL', 2);
+
+/**
  * Implementation of hook_init().
  */
 function project_issue_init() {
@@ -61,20 +76,27 @@ function project_issue_menu() {
     'type' => MENU_NORMAL_ITEM,
     'file' => 'includes/statistics.inc',
   );
-  $path = 'project/issues/subscribe-mail';
-  if (!variable_get('project_issue_global_subscribe_page', TRUE)) {
-    // If we don't want the global subscribe page, require an argument.
-    $path .= '/%';
-  }
-  $items[$path] = array(
+
+  // Project issue subscriptions.
+  $items['project/issues/subscribe-mail/%'] = array(
     'title' => 'Subscribe',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('project_issue_subscribe', 3),
+    'page arguments' => array('project_issue_subscriptions_project_form', 3),
     'access callback' => 'project_issue_menu_access',
     'access arguments' => array('auth'),
     'type' => MENU_NORMAL_ITEM,
     'file' => 'includes/subscribe.inc',
   );
+  $items['user/%user/subscriptions'] = array(
+    'title' => 'Subscriptions',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('project_issue_subscriptions_user_form', 1),
+    'access callback' => 'project_issue_menu_access',
+    'access arguments' => array('auth'),
+    'file' => 'includes/subscribe.inc',
+  );
+
+  // Search.
   if (module_exists('search')) {
     $items['search/issues'] = array(
       'title' => 'Issues',
@@ -297,10 +319,10 @@ function project_issue_theme() {
         'change' => NULL,
       ),
     ),
-    'project_issue_subscribe' => array(
-      'file' => 'issue.inc',
+    'project_issue_subscriptions_projects_table' => array(
+      'file' => 'includes/subscribe.inc',
       'arguments' => array(
-        'form' => NULL,
+        'element' => NULL,
       ),
     ),
     'project_issue_summary' => array(
@@ -866,6 +888,73 @@ function _project_issue_followup_get_user() {
 }
 
 /**
+ * Loads user account settings for project issue subscriptions.
+ *
+ * @param $account
+ *   A user account object to attach project issue subscription settings to.
+ *   Required properties:
+ *   - uid: The ID of the user account.
+ *   Attached properties:
+ *   - project_issue_subscriptions: An associative array containing:
+ *     - level: An integer denoting the user's global issue subscription level:
+ *       - PROJECT_ISSUE_SUBSCRIPTIONS_NONE
+ *       - PROJECT_ISSUE_SUBSCRIPTIONS_FLAGGED
+ *       - PROJECT_ISSUE_SUBSCRIPTIONS_ALL
+ */
+function project_issue_subscriptions_user_settings_load($account) {
+  // Setup defaults.
+  $defaults = array(
+    'level' => PROJECT_ISSUE_SUBSCRIPTIONS_NONE,
+  );
+
+  // For existing accounts, load account settings.
+  if (!empty($account->uid)) {
+    $settings = db_fetch_array(db_query("SELECT * FROM {project_issue_subscriptions_user} WHERE uid = %d", array($account->uid)));
+    $settings = ($settings ? $settings : array());
+    $account->project_issue_subscriptions = array_merge($defaults, $settings);
+  }
+  // Otherwise, attach default settings.
+  else {
+    $account->project_issue_subscriptions = $defaults;
+  }
+}
+
+/**
+ * Saves user account settings for project issue subscriptions.
+ *
+ * @param $account
+ *   A user account object containing at least the properties:
+ *   - uid: The ID of the user account.
+ *   - project_issue_subscriptions: An associative array containing:
+ *     - level: An integer denoting the user's global issue subscription level.
+ *
+ * @see project_issue_subscriptions_user_settings_load()
+ */
+function project_issue_subscriptions_user_settings_save($account) {
+  db_query("UPDATE {project_issue_subscriptions_user} SET level = %d WHERE uid = %d", array(
+    $account->project_issue_subscriptions['level'],
+    $account->uid,
+  ));
+  if (!db_affected_rows()) {
+    db_query("INSERT INTO {project_issue_subscriptions_user} (uid, level) VALUES (%d, %d)", array(
+      $account->uid,
+      $account->project_issue_subscriptions['level'],
+    ));
+  }
+}
+
+/**
+ * Implements hook_user().
+ */
+function project_issue_user($op, $edit, $account) {
+  if ($op == 'delete') {
+    db_query("DELETE FROM {project_issue_subscriptions_user} WHERE uid = %d", $account->uid);
+    db_query("DELETE FROM {project_issue_subscriptions_project} WHERE uid = %d", $account->uid);
+    db_query("DELETE FROM {project_issue_project_maintainer} WHERE uid = %d", $account->uid);
+  }
+}
+
+/**
  * hook_nodeapi() implementation. This just decides what type of node
  * is being passed, and calls the appropriate type-specific hook.
  *
@@ -1834,13 +1923,6 @@ function project_issue_query_result_links($project_arg = NULL) {
         'attributes' => array('title' => t('See statistics about issues.')),
       );
     }
-    if (!empty($user->uid) && variable_get('project_issue_global_subscribe_page', TRUE)) {
-      $links['subscribe'] = array(
-        'title' => t('Subscribe'),
-        'href' => "project/issues/subscribe-mail",
-        'attributes' => array('title' => t('Receive e-mail updates about issues.')),
-      );
-    }
   }
   else {
     // We know the project, make project-specific links.
