diff --git generate/project_issue_generate.inc generate/project_issue_generate.inc
index d7d8360..2385129 100644
--- generate/project_issue_generate.inc
+++ generate/project_issue_generate.inc
@@ -199,7 +199,7 @@ function _project_issue_generate_get_field($field, $pool_size = 100) {
       return array_keys(project_issue_category());
 
     case 'priorities':
-      return project_issue_priority();
+      return project_issue_priorities();
 
     case 'users':
       // Determine what role ids have permission to create project_issue nodes.
diff --git includes/admin.batch_confirm.inc includes/admin.batch_confirm.inc
new file mode 100644
index 0000000..b5dacfb
--- /dev/null
+++ includes/admin.batch_confirm.inc
@@ -0,0 +1,66 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Batch operations for updating denormalized issue priority weight and status.
+ */
+
+/**
+ * Run a SQL query in batch for each row in the {project_issues} table.
+ *
+ * This function will find the range of all rows in the {project_issues} table
+ * and run a query against that table incerementally, 100 rows at a time. This
+ * function is intended to be used iwth batch_set().
+ *
+ * @param $sql
+ *   A SQL query intended to be run for each row in the {project_issues} table.
+ *   Usually an update query, it is recommended to include 'BETWEEN %d AND %d'
+ *   to prevent timeouts.
+ * @param $base_arguments
+ *   An array of base arguments to pass to the query. The first and last nid of
+ *   the current range are appended to the end of this query, for specifying a
+ *   range with BETWEEN.
+ * @param $context
+ *   An array containing information about batch progress. $context['sandbox']
+ *   contains the following keys which will be populated after this function has
+ *   been called:
+ *     'min' The minimum value, or starting point.
+ *     'max' The maximum value, or end point.
+ *     'current' The nid of the last row that has been updated.
+ *     'finished' Will contain a percentage of total rows processed, and will be
+ *       set to 1 when all rows have been processed.
+ */
+function _project_issue_batch_update($sql, $base_arguments, &$context = array()) {
+  $last_nid = 0;
+  $first_nid = 0;
+  if (!isset($context['sandbox']['min'])) {
+    // Avoid COUNT(*) like hell.
+    $context['sandbox']['max'] = db_result(db_query('SELECT MAX(nid) FROM {project_issues}'));
+    // We will use > so use - 1 when choosing the smallest nid.
+    $context['sandbox']['min'] = db_result(db_query('SELECT MIN(nid) - 1 FROM {project_issues}'));
+    $context['sandbox']['current'] = $context['sandbox']['min'];
+  }
+  $arguments = $base_arguments;
+  // MySQL does not support LIMIT & IN/ALL/ANY/SOME subquery so we do the hard
+  // work ourselves: find 100 nids and record the first and the last.
+  $results = db_query_range('SELECT nid FROM {project_issues} WHERE nid > %d ORDER BY nid ASC', $context['sandbox']['current'], 0, 100);
+  while ($node = db_fetch_object($results)) {
+    if (!isset($first_nid)) {
+      $first_nid = $node->nid;
+    }
+    $last_nid = $node->nid;
+  }
+  $arguments[] = $first_nid;
+  $arguments[] = $last_nid;
+  db_query($sql, $arguments);
+  // Note that we do not count exactly as there can be holes. That's still
+  // better than running COUNT() on large datasets.
+  if ($last_nid < $context['sandbox']['max']) {
+    $context['finished'] = ($last_nid - $context['sandbox']['min']) / ($context['sandbox']['max'] - $context['sandbox']['min']);
+    $context['sandbox']['current'] = $last_nid;
+  }
+  else {
+    $context['finished'] = 1;
+  }
+}
diff --git includes/admin.issue_priority.inc includes/admin.issue_priority.inc
new file mode 100644
index 0000000..a928b06
--- /dev/null
+++ includes/admin.issue_priority.inc
@@ -0,0 +1,203 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Code for the issue priorities admin configuration form.
+ */
+
+function project_issue_delete_priority_confirm(&$form_state, $priority_id) {
+  // Helper functions are in issue.inc
+  require_once drupal_get_path('module', 'project_issue') .'/issue.inc';
+  $schema = drupal_get_schema('project_issues');
+  if (!function_exists('project_issue_priority') || !isset($schema['fields']['priority']) || !db_table_exists('project_issue_priority')) {
+    drupal_access_denied();
+    return;
+  }
+  $priorities = project_issue_priorities();
+  $name = $priorities[$priority_id];
+  unset($priorities[$priority_id]);
+
+  // $column is verified to exist
+  $total = db_result(db_query("SELECT COUNT(nid) AS total FROM {project_issues} WHERE priority = %d", $priority_id));
+  if ($total > 0) {
+    $form['new_pid'] = array(
+      '#type' => 'select',
+      '#title' => t('Reassign priority'),
+      '#default_value' => $priority_id,
+      '#options' => $priorities,
+      '#description' => format_plural($total, 'There is 1 existing issue assigned @name priority. Please select a new priority for this issue.', 'There are @count existing issues assigned @name priority. Please select a new priority for these issues.', array('@name' => $name)),
+    );
+  }
+  $form['pid'] = array(
+    '#type' => 'value',
+    '#value' => $priority_id,
+  );
+  $form['name'] = array(
+    '#type' => 'hidden',
+    '#value' => $name,
+  );
+  return confirm_form(
+    $form,
+    t('Are you sure you want to delete the priority %name?', array('%name' => $name)),
+    'admin/project/project-issue-priority',
+    t('This action cannot be undone.'),
+    t('Delete'), t('Cancel')
+  );
+}
+
+function project_issue_admin_priority_form(&$form_state) {
+  $result = db_query('SELECT priority, name, weight FROM {project_issue_priority} ORDER BY weight');
+  $max = 15;
+  $delta = 0;
+  while ($priority = db_fetch_object($result)) {
+    // We need to ensure the weight options include the maximum existing
+    // weight.
+    $max = max($delta, $priority->weight);
+    $form['priority'][$priority->priority]['name'] = array(
+      '#type' => 'textfield',
+      '#default_value' => $priority->name,
+      '#size' => 20,
+      '#maxlength' => 255,
+    );
+    $form['priority'][$priority->priority]['weight'] = array(
+      '#type' => 'weight',
+      '#default_value' => $priority->weight,
+      '#delta' => &$delta,
+      '#attributes' => array('class' => 'project-issue-priority-weight'),
+    );
+    $form['delete'][$priority->priority]['#value'] = l(t('Delete'), 'admin/project/project-issue-priority/delete/'. $priority->priority);
+  }
+  $delta = $max + 1;
+  $form['priority'][0]['name'] = array(
+    '#type' => 'textfield',
+    '#default_value' => '',
+    '#size' => 20,
+    '#maxlength' => 255,
+  );
+  $form['priority'][0]['weight'] = array(
+    '#type' => 'weight',
+    '#default_value' => $max + 1, // Make sure that the new item has a weight higher than highest priority since new item appears at bottom of form by default..
+    '#delta' => $max + 1,
+    '#attributes' => array('class' => 'project-issue-priority-weight'),
+  );
+  $form['delete'][0]['#value'] = '';
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+  $form['#tree'] = TRUE;
+  return $form;
+}
+
+/**
+ * Submit handler for project_issue_admin_states_form.
+ */
+function project_issue_admin_priority_form_submit($form, &$form_state) {
+  $offset = 0;
+  foreach ($form_state['values']['priority'] as $priority_id => $value) {
+    $offset = min($offset, $value['weight']);
+  }
+  // $offset at this point contains the smallest weight. If that's below zero,
+  // then adding it to every weight will result in unsigned weights. Otherwise
+  // such addition is not necessary.
+  $offset = $offset < 0 ? -$offset : 0;
+  // Update existing priorities.
+  $update_expression = '';
+  $weights = array();
+  foreach ($form_state['values']['priority'] as $priority_id => $value) {
+    $weight = $value['weight'] + $offset;
+    $form_state['values']['priority'][$priority_id]['weight'] = $weight;
+    // Update existing.
+    if ($priority_id) {
+      $priority = db_fetch_object(db_query('SELECT priority, name, weight FROM {project_issue_priority} WHERE priority = %d', $priority_id));
+      $update = $priority->name !== $value['name'];
+      // Check to see whether the record needs updating. We love PHP and '0'.
+      if ((string)$priority->weight !== (string)$weight) {
+        $update_expression .= " WHEN %d THEN %d";
+        $weights[] = $priority->priority;
+        $weights[] = $weight;
+        $update = TRUE;
+      }
+      if ($update) {
+        db_query("UPDATE {project_issue_priority} SET name = '%s', weight = %d WHERE priority = %d", $value['name'], $weight, $priority_id);
+      }
+    }
+    // Add new priority.
+    elseif ($value['name']) {
+      // Check to see whether the state already exists:
+      $issue_state = db_result(db_query("SELECT COUNT(*) FROM {project_issue_priority} WHERE name = '%s'", $value['name']));
+      if (empty($issue_state)) {
+        db_query("INSERT INTO {project_issue_priority} (name, weight) VALUES ('%s', %d)", $value['name'], $weight);
+      }
+      else {
+        drupal_set_message(t('Priority %priority already exists.', array ('%priority' => $value['name'])), 'error');
+      }
+    }
+  }
+  if ($weights) {
+    $weight_sql = "UPDATE {project_issues} SET priority_weight = CASE priority $update_expression ELSE priority_weight END WHERE nid BETWEEN %d AND %d";
+    $batch = array(
+      'operations' => array(
+        array('_project_issue_batch_update', array($weight_sql, $weights)),
+      ),
+      'title' => t('Processing'),
+      // We use a single multi-pass operation, so the default
+      // 'Remaining x of y operations' message will be confusing here.
+      'progress_message' => '',
+      'error_message' => t('The update has encountered an error.'),
+      // The operations do not live in the .module file, so we need to
+      // tell the batch engine which file to load before calling them.
+      'file' => drupal_get_path('module', 'project_issue') .'/includes/admin.batch_confirm.inc',
+    );
+    batch_set($batch);
+  }
+}
+
+function project_issue_delete_priority_confirm_submit($form, &$form_state) {
+  db_query('DELETE FROM {project_issue_priority} WHERE priority = %d', $form_state['values']['pid']);
+  $form_state['redirect'] = 'admin/project/project-issue-priority';
+  drupal_set_message(t('Project issue priority %name deleted.', array('%name' => $form_state['values']['name'])));
+  if (isset($form_state['values']['new_pid'])) {
+    $priority_weight = db_result(db_query('SELECT weight FROM {project_issue_priority} WHERE priority = %d', $form_state['values']['new_pid']));
+    $arguments = array($form_state['values']['new_pid'], $priority_weight, $form_state['values']['pid']);
+    $update_sql = "UPDATE {project_issues} SET priority = %d, priority_weight = %d WHERE priority = %d AND nid BETWEEN %d AND %d";
+    $batch = array(
+      'operations' => array(
+        array('_project_issue_batch_update', array($update_sql, $arguments)),
+      ),
+      'title' => t('Processing'),
+      // We use a single multi-pass operation, so the default
+      // 'Remaining x of y operations' message will be confusing here.
+      'progress_message' => '',
+      'error_message' => t('The update has encountered an error.'),
+      // The operations do not live in the .module file, so we need to
+      // tell the batch engine which file to load before calling them.
+      'file' => drupal_get_path('module', 'project_issue') .'/includes/admin.batch_confirm.inc',
+    );
+    batch_set($batch);
+  }
+}
+
+function theme_project_issue_admin_priority_form($form) {
+  drupal_add_tabledrag('project-issue-admin-priority-table', 'order', 'self', 'project-issue-priority-weight');
+  $header = array(
+    t('Priority'),
+    t('Weight'),
+    t('Operations'),
+  );
+  foreach (element_children($form['priority']) as $key) {
+    $rows[] = array(
+      'class' => 'draggable',
+      'data' => array(
+        drupal_render($form['priority'][$key]['name']),
+        drupal_render($form['priority'][$key]['weight']),
+        drupal_render($form['delete'][$key]),
+      ),
+    );
+  }
+  $output = '<div>' . theme('table', $header, $rows, array('id' => 'project-issue-admin-priority-table')) . '</div>';
+  $output .= drupal_render($form);
+  return $output;
+}
diff --git includes/admin.issue_status.inc includes/admin.issue_status.inc
index 3ef42e6..1c8219c 100644
--- includes/admin.issue_status.inc
+++ includes/admin.issue_status.inc
@@ -6,7 +6,7 @@
  * Code for the issue status admin configuration form.
  */
 
-function theme_project_issue_admin_states_form($form) {
+function theme_project_issue_admin_state_form($form) {
   drupal_add_tabledrag('project-issue-status-admin-table', 'order', 'self', 'project-issue-status-weight');
   $header = array(
     array('data' => t('ID')),
@@ -47,7 +47,7 @@ function theme_project_issue_admin_states_form($form) {
   return $output;
 }
 
-function project_issue_admin_states_form(&$form_state) {
+function project_issue_admin_state_form(&$form_state) {
   $result = db_query('SELECT * FROM {project_issue_state} ORDER BY weight');
   $default_state = variable_get('project_issue_default_state', 1);
   $default_states = project_issue_default_states();
@@ -117,7 +117,7 @@ function project_issue_admin_states_form(&$form_state) {
 /**
  * Submit handler for project_issue_admin_states_form.
  */
-function project_issue_admin_states_form_submit($form, &$form_state) {
+function project_issue_admin_state_form_submit($form, &$form_state) {
   // Check for and apply changes or additions to project issue status options.
   if (isset($form_state['values']['default_state'])) {
     variable_set('project_issue_default_state', $form_state['values']['default_state']);
@@ -156,7 +156,7 @@ function project_issue_delete_state_confirm(&$form_state, $sid) {
       '#title' => t('Reassign status'),
       '#default_value' => $sid,
       '#options' => $states,
-      '#description' => t('There are !total existing issues with the status of @name. Please select a new status for these issues.', array('!total' => $total, '@name' => $name)),
+      '#description' => format_plural($total, 'There is 1 existing issue assigned to @name status. Please select a new status for this issue.', 'There are @count existing issues assigned to @name status. Please select a new status for these issues.', array('@name' => $name)),
     );
   }
   $form['sid'] = array(
@@ -183,11 +183,25 @@ function project_issue_delete_state_confirm_validate($form, &$form_state) {
 }
 
 function project_issue_delete_state_confirm_submit($form, &$form_state) {
+  db_query('DELETE FROM {project_issue_state} WHERE sid = %d', $form_state['values']['sid']);
+  $form_state['redirect'] = 'admin/project/project-issue-status';
+  drupal_set_message(t('Project issue status %name deleted.', array('%name' => $form_state['values']['name'])));
   if ($form_state['values']['new_sid']) {
-    db_query('UPDATE {project_issues} SET sid = %d WHERE sid = %d', $form_state['values']['new_sid'], $form_state['values']['sid']);
+    $arguments = array($form_state['values']['new_sid'], $form_state['values']['sid']);
+    $update_sql = "UPDATE {project_issues} SET sid = %d WHERE sid = %d AND nid BETWEEN %d AND %d";
+    $batch = array(
+      'operations' => array(
+        array('_project_issue_batch_update', array($update_sql, $arguments)),
+      ),
+      'title' => t('Processing'),
+      // We use a single multi-pass operation, so the default
+      // 'Remaining x of y operations' message will be confusing here.
+      'progress_message' => '',
+      'error_message' => t('The update has encountered an error.'),
+      // The operations do not live in the .module file, so we need to
+      // tell the batch engine which file to load before calling them.
+      'file' => drupal_get_path('module', 'project_issue') .'/includes/admin.batch_confirm.inc',
+    );
+    batch_set($batch);
   }
-  db_query('DELETE FROM {project_issue_state} WHERE sid = %d', $form_state['values']['sid']);
-  drupal_set_message(t('Project issue status %issue deleted.', array('%issue' => $form_state['values']['name'])));
-  $form_state['redirect'] ='admin/project/project-issue-status';
 }
-
diff --git includes/comment.inc includes/comment.inc
index f9dae37..abb03ba 100644
--- includes/comment.inc
+++ includes/comment.inc
@@ -83,7 +83,7 @@ function project_issue_comment(&$arg, $op) {
       // We should also invalidate the block cache for whatever project is now
       // used for this issue, since we might be deleting a comment that moved
       // an issue from one project to another.
-      $affected_projects[$current_data->pid] = 1;      
+      $affected_projects[$current_data->pid] = 1;
       break;
 
     case 'view':
@@ -571,8 +571,10 @@ function project_issue_update_by_comment($comment_data, $op) {
   // the updates to the issue with the latest available published comment.
   $comment_data = project_issue_get_newest_comment($comment_data);
 
+  $priority_weight = db_result(db_query('SELECT weight FROM {project_issue_priority} WHERE priority = %d', $comment_data->priority));
+
   // Update the issue data to reflect the new final states.
-  db_query("UPDATE {project_issues} SET pid = %d, category = '%s', component = '%s', priority = %d, rid = %d, assigned = %d, sid = %d WHERE nid = %d", $comment_data->pid, $comment_data->category, $comment_data->component, $comment_data->priority, $comment_data->rid, $comment_data->assigned, $comment_data->sid, $comment_data->nid);
+  db_query("UPDATE {project_issues} SET pid = %d, category = '%s', component = '%s', priority = %d, rid = %d, assigned = %d, sid = %d, priority_weight = %d WHERE nid = %d", $comment_data->pid, $comment_data->category, $comment_data->component, $comment_data->priority, $comment_data->rid, $comment_data->assigned, $comment_data->sid, $priority_weight, $comment_data->nid);
 
   // Update the issue title.
   $node = node_load($comment_data->nid, NULL, TRUE);  // Don't use cached since we changed data above.
diff --git includes/issue_node_form.inc includes/issue_node_form.inc
index 61364ab..8a80ad7 100644
--- includes/issue_node_form.inc
+++ includes/issue_node_form.inc
@@ -215,7 +215,7 @@ function _project_issue_form($node, $form_state, $include_metadata_fields = FALS
       }
     }
     $categories = array_merge(array(t('<none>')), project_issue_category(0, 0));
-    $priorities = project_issue_priority();
+    $priorities = project_issue_priorities();
     $states = project_issue_state(0, TRUE, !empty($node->nid) && ($node->uid == $user->uid), $node->project_issue['sid']);
     $assigned = project_issue_assigned_choices($node);
   }
@@ -238,6 +238,7 @@ function _project_issue_form($node, $form_state, $include_metadata_fields = FALS
     );
   }
 
+  $priority = $node->project_issue['priority'] ? $node->project_issue['priority'] : variable_get('project_issue_priority_default', 2);
   if ($allow_metadata_changes) {
     $form['project_info'] = array(
       '#type' => 'fieldset',
@@ -286,7 +287,7 @@ function _project_issue_form($node, $form_state, $include_metadata_fields = FALS
     $form['issue_info']['priority'] = array(
       '#type' => 'select',
       '#title' => t('Priority'),
-      '#default_value' => $node->project_issue['priority'] ? $node->project_issue['priority'] : 2,
+      '#default_value' => $priority,
       '#options' => $priorities,
     );
     $form['issue_info']['assigned'] = array(
@@ -340,7 +341,7 @@ function _project_issue_form($node, $form_state, $include_metadata_fields = FALS
     );
     $form['project_issue']['priority'] = array(
       '#type' => 'value',
-      '#value' => $node->project_issue['priority'],
+      '#value' => $priority,
     );
     $form['project_issue']['assigned'] = array(
       '#type' => 'value',
@@ -475,10 +476,11 @@ function _project_issue_insert($node) {
     $original_issue_data->$field = $node->$field;
   }
 
-  db_query("INSERT INTO {project_issues} (nid, pid, category, component, priority, rid, assigned, sid, original_issue_data, last_comment_id, db_lock) VALUES (%d, %d, '%s', '%s', %d, %d, %d, %d, '%s', %d, %d)", $node->nid, $node->pid, $node->category, $node->component, $node->priority, $node->rid, $node->assigned, $node->sid, serialize($original_issue_data), 0, 0);
+  $priority_weight = db_result(db_query('SELECT weight FROM {project_issue_priority} WHERE priority = %d', $node->priority));
+
+  db_query("INSERT INTO {project_issues} (nid, pid, category, component, priority, rid, assigned, sid, original_issue_data, last_comment_id, db_lock, priority_weight) VALUES (%d, %d, '%s', '%s', %d, %d, %d, %d, '%s', %d, %d, %d)", $node->nid, $node->pid, $node->category, $node->component, $node->priority, $node->rid, $node->assigned, $node->sid, serialize($original_issue_data), 0, 0, $priority_weight);
 
   // Invalidate the "Issue cockpit" block cache for this project, since the
   // new issue will have altered the summary totals.
   cache_clear_all('project_issue_cockpit_block:'. $node->pid, 'cache');
 }
-
diff --git includes/mail.inc includes/mail.inc
index 5637c37..880cdc9 100644
--- includes/mail.inc
+++ includes/mail.inc
@@ -62,7 +62,7 @@ function _project_issue_mailhandler($node, $result, $i, $header, $mailbox) {
               }
               break;
             case 'priority':
-              if (($priority = array_search($node->project_issue[$text], project_issue_priority()))) {
+              if (($priority = array_search($node->project_issue[$text], project_issue_priorities()))) {
                 $entry->project_issue['priority'] = $priority;
               }
               break;
diff --git issue.inc issue.inc
index fa1755d..1d73f0b 100644
--- issue.inc
+++ issue.inc
@@ -235,9 +235,39 @@ function project_issue_default_states() {
   return $defaults;
 }
 
-function project_issue_priority($priority = 0) {
-  $priorities = array(1 => t('critical'), t('normal'), t('minor'));
-  return $priority ? $priorities[$priority] : $priorities;
+/**
+ * Return all available issue priorities, sorted by weight.
+ *
+ * @return
+ *   An array of priorities keyed by id.
+ */
+function project_issue_priorities() {
+  static $priorities;
+  if (!isset($priorities)) {
+    $result = db_query('SELECT priority, name FROM {project_issue_priority} ORDER BY weight');
+    while ($object = db_fetch_object($result)) {
+      $priorities[$object->priority] = $object->name;
+    }
+  }
+  return $priorities;
+}
+
+/**
+ * Return the name of a priority for a given id.
+ *
+ * @param $priority
+ *   The id of the priority.
+ * @return
+ *   The name of the priority.
+ */
+function project_issue_priority($priority) {
+  $priorities = project_issue_priorities();
+  if ($priority && array_key_exists($priority, $priorities)) {
+    return $priorities[$priority];
+  }
+  else {
+    return t('[deleted priority]');
+  } 
 }
 
 function project_issue_category($category = 0, $plural = 1) {
diff --git project_issue.install project_issue.install
index aeab527..b6c178c 100644
--- project_issue.install
+++ project_issue.install
@@ -110,13 +110,20 @@ function project_issue_schema() {
         'default' => '',
       ),
       'priority' => array(
-        'description' => 'The priority for this issue.',
+        'description' => 'Current {project_issue_priority}.priority of this issue.',
         'type' => 'int',
         'size' => 'tiny',
         'unsigned' => 1,
         'not null' => TRUE,
         'default' => 0,
       ),
+      'priority_weight' => array(
+        'description' => 'The denormalized weight from {project_issue_priority}.weight.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
       'rid' => array(
         'description' => 'The {project_release_nodes}.rid (version identifier) for this issue (only used in conjunction with the project_release module).',
         'type' => 'int',
@@ -199,7 +206,7 @@ function project_issue_schema() {
         'default' => '',
       ),
       'priority' => array(
-        'description' => 'The priority for this issue after this comment was made.',
+        'description' => 'The {project_issue_priority}.priority of this issue after this comment was made.',
         'type' => 'int',
         'not null' => TRUE,
         'default' => 0,
@@ -249,6 +256,32 @@ function project_issue_schema() {
     ),
   );
 
+  $schema['project_issue_priority'] = array(
+    'description' => 'The available options for the priority values for issues.',
+    'fields' => array(
+      'priority' => array(
+        'description' => 'Primary Key: Unique id for this priority.',
+        'type' => 'serial',
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'Display-friendly name for this priority.',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'weight' => array(
+        'description' => 'Weight for this priority, used when ordering issues by priority. Denormalized into {project_issues}.priority_weight.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('priority'),
+  );
+
   $schema['project_subscriptions'] = array(
     'description' => 'Table keeping track of per-user project_issue subscriptions.',
     'fields' => array(
@@ -364,6 +397,9 @@ function project_issue_install() {
   variable_set('comment_upload_project_issue', 1);
   // Enable file attachments for issues.
   variable_set('upload_project_issue', 1);
+  db_query("INSERT INTO {project_issue_priority} (priority, name, weight) VALUES (1, 'critical', 1)");
+  db_query("INSERT INTO {project_issue_priority} (priority, name, weight) VALUES (2, 'normal', 2)");
+  db_query("INSERT INTO {project_issue_priority} (priority, name, weight) VALUES (3, 'minor', 3)");
 }
 
 /**
@@ -446,3 +482,57 @@ function project_issue_update_6002() {
   return $ret;
 }
 
+/**
+ * Add table for project_issue_priority, and insert default values.
+ */
+function project_issue_update_6003() {
+  $ret = array();
+  $table = array(
+    'description' => 'The issue priorities.',
+    'fields' => array(
+      'priority' => array(
+        'description' => 'Primary Key: Unique id for this priority.',
+        'type' => 'serial',
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'Display-friendly name for this priority.',
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'weight' => array(
+        'description' => 'Weight for this priority, used when ordering issues by priority. Denormalized into {project_issues}.priority_weight.',
+        'type' => 'int',
+        'size' => 'tiny',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('priority'),
+  );
+  db_create_table($ret, 'project_issue_priority', $table);
+  db_query("INSERT INTO {project_issue_priority} (priority, name, weight) VALUES (1, 'critical', 1)");
+  db_query("INSERT INTO {project_issue_priority} (priority, name, weight) VALUES (2, 'normal', 2)");
+  db_query("INSERT INTO {project_issue_priority} (priority, name, weight) VALUES (3, 'minor', 3)");
+
+  db_add_field($ret, 'project_issues', 'priority_weight', array('type' => 'int', 'not null' => TRUE, 'default' => '0', 'size' => 'tiny'));
+  return $ret;
+}
+
+/**
+ * Update project_issues, with denormalized priority weight.
+ */
+function project_issue_update_6004(&$sandbox) {
+  $ret = array();
+  // Reconstruct a batch context.
+  $context = array('sandbox' => &$sandbox);
+  // Load the include.
+  require_once drupal_get_path('module', 'project_issue') .'/includes/admin.batch_confirm.inc';
+  // Call the batch upgrade which will set its variables in the sandbox.
+  _project_issue_batch_update('UPDATE project_issues SET priority_weight = priority WHERE nid BETWEEN %d AND %d', array(), $context);
+  // Pass back progress.
+  $ret['#finished'] = $context['finished'];
+  return $ret;
+}
diff --git project_issue.module project_issue.module
index d01d946..c9f1ba9 100644
--- project_issue.module
+++ project_issue.module
@@ -99,12 +99,33 @@ function project_issue_menu() {
     'file' => 'includes/admin.settings.inc',
   );
 
+  // Administrative pages
+  $items['admin/project/project-issue-priority'] = array(
+    'title' => 'Project issue priorities',
+    'description' => 'Configure what issue priorities should be used on your site.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('project_issue_admin_priority_form'),
+    'access arguments' => array('administer projects'),
+    'weight' => 1,
+    'type' => MENU_NORMAL_ITEM,
+    'file' => 'includes/admin.issue_priority.inc',
+  );
+  $items['admin/project/project-issue-priority/delete'] = array(
+    'title' => 'Delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('project_issue_delete_priority_confirm', 4),
+    'access arguments' => array('administer projects'),
+    'type' => MENU_CALLBACK,
+    'file' => 'includes/admin.issue_priority.inc'
+  );
+
+
   // Administer issue status settings
   $items['admin/project/project-issue-status'] = array(
     'title' => 'Project issue status options',
     'description' => 'Configure what issue status values should be used on your site.',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('project_issue_admin_states_form'),
+    'page arguments' => array('project_issue_admin_state_form'),
     'access arguments' => array('administer projects'),
     'type' => MENU_NORMAL_ITEM,
     'weight' => 1,
@@ -113,7 +134,7 @@ function project_issue_menu() {
   $items['admin/project/project-issue-status/delete'] = array(
     'title' => 'Delete',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('project_issue_delete_state_confirm', 4),
+    'page arguments' => array('project_issue_delete_state_confirm',  4),
     'access arguments' => array('administer projects'),
     'type' => MENU_CALLBACK,
     'file' => 'includes/admin.issue_status.inc'
@@ -317,12 +338,18 @@ function project_issue_theme() {
         'summary_links' => NULL,
       ),
     ),
-    'project_issue_admin_states_form' => array(
+    'project_issue_admin_state_form' => array(
       'file' => 'includes/admin.issue_status.inc',
       'arguments' => array(
         'form' => NULL,
       ),
     ),
+    'project_issue_admin_priority_form' => array(
+      'file' => 'includes/admin.issue_priority.inc',
+      'arguments' => array(
+        'form' => NULL,
+      ),
+    ),
     'project_issue_project_edit_form' => array(
       'file' => 'includes/project_edit_issues.inc',
       'arguments' => array(
@@ -525,7 +552,7 @@ function project_issue_access($op, $node, $account) {
  * Helper to trim all elements in an array.
  */
 function project_issue_trim(&$item, $key) {
-  $item = trim($item);  
+  $item = trim($item);
 }
 
 /**
@@ -619,7 +646,7 @@ function project_issue_add_auto_followup($changes) {
  *     );
  *
  * @return
- *   TRUE if the comment was successfully added to the requested issue, 
+ *   TRUE if the comment was successfully added to the requested issue,
  *   otherwise FALSE.
  */
 function project_issue_add_followup($changes) {
@@ -1770,4 +1797,3 @@ function project_issue_project_page_link_alter(&$links, $node) {
     $links['development']['links'] = $patches + $links['development']['links'];
   }
 }
-
diff --git project_issue.test project_issue.test
index 79bc0b4..0192238 100644
--- project_issue.test
+++ project_issue.test
@@ -168,7 +168,7 @@ class ProjectIssueCommentTestCase extends ProjectIssueWebTestCase {
       'pid' => 'project_info[pid]',
     );
     foreach ($map as $issue_key => $post_key) {
-      $this->assertEqual($issue->project_issue[$issue_key], $post[$post_key], t('Making sure comment form is correct'));
+      $this->assertEqual($issue->project_issue[$issue_key], $post[$post_key], t('Making sure comment form is correct.'));
     }
   }
 
@@ -182,4 +182,145 @@ class ProjectIssueCommentTestCase extends ProjectIssueWebTestCase {
     $comment = $this->createIssueComment($issue);
     $this->assertText($comment['comment'], t('Body found'));
   }
+
+  
 }
+
+class ProjectIssuePriorityTestCase extends ProjectIssueWebTestCase {
+  /**
+   * A user who can maintain project issue administrative settings, projects, and create issues.
+   */
+  protected $maintain_user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Project issue priority',
+      'description' => 'Test issue priority settings and functionality.',
+      'group' => 'Project Issue',
+    );
+  }
+
+  function setUp() {
+    parent::setUp();
+
+    $this->maintain_user = $this->drupalCreateUser(array('administer projects', 'maintain projects', 'create project issues', 'access project issues', 'access projects'));
+    $this->drupalLogin($this->maintain_user);
+  }
+
+  function assertAdminPrioritiesForm($values) {
+    $pass = TRUE;
+    $this->drupalGet('admin/project/project-issue-priority');
+    $forms = $this->xpath('//form');
+    $form = $forms[0];
+
+    $post = $edit = $upload = array();
+    $this->handleForm($post, $edit, $upload, NULL, $form);
+
+    foreach ($values as $key => $priority) {
+      $result = $this->assertEqual($post['priority[' . $key .'][name]'], $priority['name'], t('The name for the priority is correct.'));
+      $result = $this->assertEqual($post['priority[' . $key .'][weight]'], $priority['weight'], t('The weight for the priority is correct.'));
+      
+    }
+  }
+
+  /**
+   * Test the project issue priority admin form.
+   */
+  function testProjectIssuePrioritySettings() {
+    // This matches the defaults setup in project_issue_install().
+    $priorities = array(
+      1 => array(
+        'name' => 'critical',
+        'weight' => 1,
+      ),
+      2 => array(
+        'name' => 'normal',
+        'weight' => 2,
+      ),
+      3 => array(
+        'name' => 'minor',
+        'weight' => 3,
+      ),
+    );
+    $this->assertAdminPrioritiesForm($priorities);
+
+    // Test reordering ability
+    $edit = array();
+    $edit['priority[1][weight]'] = -2;
+    $edit['priority[2][weight]'] = -3;
+    $edit['priority[3][weight]'] = -1;
+    $this->drupalPost('admin/project/project-issue-priority', $edit, t('Save'));
+
+    // Check new values
+    $priorities = array(
+      1 => array(
+        'name' => 'critical',
+        'weight' => 1,
+      ),
+      2 => array(
+        'name' => 'normal',
+        'weight' => 0,
+      ),
+      3 => array(
+        'name' => 'minor',
+        'weight' => 2,
+      ),
+    );
+    $this->assertAdminPrioritiesForm($priorities);
+    
+    // Add a new priority, and check the form results
+    $edit = array();
+    $edit['priority[0][name]'] = 'major';
+    $this->drupalPost('admin/project/project-issue-priority', $edit, t('Save'));
+
+    // Check new values
+    $priorities = array(
+      1 => array(
+        'name' => 'critical',
+        'weight' => 1,
+      ),
+      2 => array(
+        'name' => 'normal',
+        'weight' => 0,
+      ),
+      3 => array(
+        'name' => 'minor',
+        'weight' => 2,
+      ),
+      4 => array(
+        'name' => 'major',
+        'weight' => 3,
+      ),
+    );
+    $this->assertAdminPrioritiesForm($priorities);
+
+    // Test deleting a priority without any issues.
+    $this->drupalGet('admin/project/project-issue-priority/delete/4');
+    $this->assertNoText('Reassign priority', t('Issue confirm form is displayed properly.'));
+    $this->drupalPost('admin/project/project-issue-priority/delete/4', array(), t('Delete'));
+    $this->assertText('Project issue priority major deleted.', t('Project issue priority has been deleted.'));
+
+    // Test that a custom priority can be assinged to an issue and is displayed correctly.
+    $edit = array();
+    $edit['priority[0][name]'] = $this->randomName(8);
+    $this->drupalPost('admin/project/project-issue-priority', $edit, t('Save'));
+    $project = $this->createProject();
+
+    $edit = array();
+    $edit['priority'] = '5';
+    $issue = $this->createIssue($project, $edit);
+    $this->drupalGet('node/'. $issue->nid);
+
+    // Check that the issue priority is displayed correctly.
+    $this->assertText($edit['priority'], t('Custom issue priority is displayed correctly.'));
+
+    // Delete the priority
+    $this->drupalGet('admin/project/project-issue-priority/delete/5');
+    $this->assertText('Reassign priority', t('Issue confirm form is displayed properly.'));
+
+    $edit = array();
+    $edit['new_pid'] = 2;
+    
+    
+  }
+}
\ No newline at end of file
diff --git views/handlers/project_issue_handler_field_issue_priority.inc views/handlers/project_issue_handler_field_issue_priority.inc
index b35de9f..7e7fcd7 100644
--- views/handlers/project_issue_handler_field_issue_priority.inc
+++ views/handlers/project_issue_handler_field_issue_priority.inc
@@ -10,5 +10,12 @@ class project_issue_handler_field_issue_priority extends views_handler_field {
     module_load_include('inc', 'project_issue', 'issue');
     return check_plain(project_issue_priority($values->{$this->field_alias}));
   }
+  
+  function click_sort($order) {
+    // Reverse the sort order since more important priorities are weighted
+    // lighter.
+    $order = ($order == 'asc') ? 'desc' : 'asc';
+    // Override the sort field, since we are storing denormalized weight.
+    $this->query->add_orderby($this->table, 'priority_weight', $order);
+  }
 }
-
diff --git views/handlers/project_issue_handler_filter_issue_priority.inc views/handlers/project_issue_handler_filter_issue_priority.inc
index 7cf6cd2..427a989 100644
--- views/handlers/project_issue_handler_filter_issue_priority.inc
+++ views/handlers/project_issue_handler_filter_issue_priority.inc
@@ -8,7 +8,7 @@
 class project_issue_handler_filter_issue_priority extends views_handler_filter_in_operator {
   function get_value_options() {
     module_load_include('inc', 'project_issue', 'issue');
-    $this->value_options = project_issue_priority();
+    $this->value_options = project_issue_priorities();
   }
 }
 
diff --git views/handlers/project_issue_handler_sort_issue_priority_weight.inc views/handlers/project_issue_handler_sort_issue_priority_weight.inc
new file mode 100644
index 0000000..f259a3f
--- /dev/null
+++ views/handlers/project_issue_handler_sort_issue_priority_weight.inc
@@ -0,0 +1,16 @@
+<?php
+// $Id$
+
+/**
+ * Sort handler for issue priority, using denormalized priority_weight column.
+ */
+class project_issue_handler_sort_issue_priority_weight extends views_handler_sort {
+  function query() {
+    $this->ensure_my_table();
+    // Reverse the sort order since more important priorities are weighted
+    // lighter.
+    $order = ($this->options['order'] == 'asc') ? 'desc' : 'asc';
+    // Override the field name, since we are storing a denormalized weight.
+    $this->query->add_orderby($this->table_alias, 'priority_weight', $order);
+  }
+}
diff --git views/project_issue.views.inc views/project_issue.views.inc
index 77be317..7b0f843 100644
--- views/project_issue.views.inc
+++ views/project_issue.views.inc
@@ -114,7 +114,7 @@ function project_issue_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'project_issue_handler_sort_issue_priority_weight',
       'help' => t("Sort by the issue's priority."),
     ),
     'filter' => array(
@@ -220,6 +220,9 @@ function project_issue_views_handlers() {
       'project_issue_handler_filter_issue_version' => array(
         'parent' => 'views_handler_filter_in_operator',
       ),
+      'project_issue_handler_sort_issue_priority_weight' => array(
+        'parent' => 'views_handler_sort',
+      ),
     ),
   );
 }
