diff --git a/election_candidate/election_candidate.admin.inc b/election_candidate/election_candidate.admin.inc
index c10439a..cef0e49 100644
--- a/election_candidate/election_candidate.admin.inc
+++ b/election_candidate/election_candidate.admin.inc
@@ -19,6 +19,53 @@ function election_candidate_manage_page(stdClass $election) {
 }
 
 /**
+ * Page callback for editing an election candidate.
+ */
+function election_candidate_page_edit($candidate) {
+
+  drupal_set_title(t('Edit candidate: %first-name %last-name', array('%first-name' => $candidate->first_name, '%last-name' => $candidate->last_name)), PASS_THROUGH);
+
+  $post = election_post_load($candidate->post_id);
+  $election = $post->election;
+  election_post_set_breadcrumb($election, $post);
+
+  module_load_include('inc', 'election_candidate', 'election_candidate.forms');
+
+  return drupal_get_form('election_candidate_form', $candidate, $post);
+}
+
+/**
+ * Page callback for adding an election candidate.
+ */
+function election_candidate_page_add($post) {
+
+  if (!is_object($post)) {
+    return MENU_NOT_FOUND;
+  }
+
+  $election = $post->election;
+  election_post_set_breadcrumb($election, $post);
+
+  $candidate_type = !empty($post->settings['candidate_type']) ? $post->settings['candidate_type'] : 'candidate';
+  $candidate = entity_get_controller('election_candidate')->create(array(
+    'type' => $candidate_type,
+    'election_id' => $election->election_id,
+    'post_id' => $post->post_id,
+    'is_new' => TRUE,
+  ));
+
+  drupal_set_title(
+    t('Add new candidate to %post', array('%post' => $post->title)),
+    PASS_THROUGH
+  );
+
+  module_load_include('inc', 'election_candidate', 'election_candidate.forms');
+
+  return drupal_get_form('election_candidate_form', $candidate, $post);
+
+}
+
+/**
  * Page callback to display total numbers of candidates per post.
  */
 function election_candidate_totals_page(stdClass $election) {
diff --git a/election_candidate/election_candidate.forms.inc b/election_candidate/election_candidate.forms.inc
index b778714..215d996 100644
--- a/election_candidate/election_candidate.forms.inc
+++ b/election_candidate/election_candidate.forms.inc
@@ -7,14 +7,11 @@
 /**
  * Form builder function for editing a single candidate.
  */
-function election_candidate_edit_form($form, &$form_state, stdClass $candidate) {
+function election_candidate_form($form, &$form_state, stdClass $candidate, stdClass $post) {
 
-  $election = election_load($candidate->election_id);
-
-  drupal_set_title(t('Edit candidate: %first-name %last-name', array('%first-name' => $candidate->first_name, '%last-name' => $candidate->last_name)), PASS_THROUGH);
-  election_set_breadcrumb($election);
+  $election = $post->election;
 
-  $form['#id'] = 'election-candidate-edit-form';
+  $form['#id'] = 'election-candidate-form';
   $form['#candidate'] = $candidate;
 
   $form_state['election'] = $election;
@@ -25,7 +22,6 @@ function election_candidate_edit_form($form, &$form_state, stdClass $candidate)
     $candidate_account = user_load($candidate->uid);
   }
 
-  $post = election_post_load($candidate->post_id);
   $post_name = election_get_post_name($election->type);
   $form['post'] = array(
     '#type' => 'item',
@@ -108,38 +104,40 @@ function election_candidate_edit_form($form, &$form_state, stdClass $candidate)
   $form['buttons']['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save'),
-    '#submit' => array('election_candidate_edit_form_submit'),
+    '#submit' => array('election_candidate_form_submit'),
   );
-  $form['buttons']['delete'] = array(
-    '#type' => 'submit',
-    '#value' => t('Delete'),
-    '#submit' => array('election_candidate_edit_form_delete_submit'),
-    '#access' => entity_access('delete', 'election_candidate', $candidate),
-    '#limit_validation_errors' => array(),
-  );
-  $cancel_path = election_candidate_uri_path($candidate);
-  if (!empty($_GET['destination'])) {
-    $options = drupal_parse_url($_GET['destination']);
-    $cancel_path = $options['path'];
+
+  if (empty($candidate->is_new)) {
+    $form['buttons']['delete'] = array(
+      '#type' => 'submit',
+      '#value' => t('Delete'),
+      '#submit' => array('election_candidate_form_delete_submit'),
+      '#access' => entity_access('delete', 'election_candidate', $candidate),
+      '#limit_validation_errors' => array(),
+    );
+    $cancel_path = election_candidate_uri_path($candidate);
+    if (!empty($_GET['destination'])) {
+      $options = drupal_parse_url($_GET['destination']);
+      $cancel_path = $options['path'];
+    }
+    $form['buttons']['cancel'] = array(
+      '#type' => 'link',
+      '#title' => t('Cancel'),
+      '#href' => $cancel_path,
+    );
   }
-  $form['buttons']['cancel'] = array(
-    '#type' => 'link',
-    '#title' => t('Cancel'),
-    '#href' => $cancel_path,
-  );
 
   form_load_include($form_state, 'inc', 'election_candidate', 'election_candidate.forms');
   $form['#validate'][] = 'election_candidate_form_validate';
-  $form['#validate'][] = 'election_candidate_edit_form_validate';
 
   return $form;
 
 }
 
 /**
- * Form submit function for the Save button in election_candidate_edit_form.
+ * Form submit function for the Save button in election_candidate_form.
  */
-function election_candidate_edit_form_submit($form, &$form_state) {
+function election_candidate_form_submit($form, &$form_state) {
 
   $candidate = &$form_state['candidate'];
 
@@ -170,12 +168,16 @@ function election_candidate_edit_form_submit($form, &$form_state) {
     )
   );
 
+  if (empty($_GET['destination'])) {
+    $form_state['redirect'] = election_candidate_uri_path($candidate);
+  }
+
 }
 
 /**
- * Form submit function for the Delete button in election_candidate_edit_form.
+ * Form submit function for the Delete button in election_candidate_form.
  */
-function election_candidate_edit_form_delete_submit($form, &$form_state) {
+function election_candidate_form_delete_submit($form, &$form_state) {
   $destination = array();
   if (isset($_GET['destination'])) {
     $destination = drupal_get_destination();
@@ -191,7 +193,7 @@ function election_candidate_edit_form_delete_submit($form, &$form_state) {
 /**
  * Form builder function to confirm the delete action for a candidate.
  */
-function election_candidate_edit_form_delete_confirm($form, &$form_state, stdClass $candidate) {
+function election_candidate_form_delete_confirm($form, &$form_state, stdClass $candidate) {
 
   $election = election_load($candidate->election_id);
 
@@ -215,7 +217,7 @@ function election_candidate_edit_form_delete_confirm($form, &$form_state, stdCla
 /**
  * Form submit function for confirming delete for an election candidate.
  */
-function election_candidate_edit_form_delete_confirm_submit($form, &$form_state) {
+function election_candidate_form_delete_confirm_submit($form, &$form_state) {
   if ($form_state['values']['confirm']) {
 
     $candidate = election_candidate_load($form_state['values']['candidate_id']);
@@ -362,17 +364,6 @@ function election_candidate_form_validate($form, &$form_state) {
 }
 
 /**
- * Validation function for editing a candidate.
- */
-function election_candidate_edit_form_validate($form, &$form_state) {
-  if (!empty($form_state['values']['new_user'])) {
-    if (!$account = user_load_by_name($form_state['values']['new_user'])) {
-      form_set_error('new_user', t('The username %name does not exist', array('%name' => $form_state['values']['new_user'])));
-    }
-  }
-}
-
-/**
  * Submit callback for the nomination form.
  */
 function election_candidate_nomination_form_submit($form, &$form_state) {
diff --git a/election_candidate/election_candidate.module b/election_candidate/election_candidate.module
index f7cb004..82f82ea 100644
--- a/election_candidate/election_candidate.module
+++ b/election_candidate/election_candidate.module
@@ -130,18 +130,6 @@ function election_candidate_permission() {
       'title' => t('Administer candidate types'),
       'description' => t('This allows the user to add/edit/delete candidate types, and manage field and display settings for candidates.'),
     ),
-    'bypass nomination schedule' => array(
-      'title' => t('Bypass nomination schedule'),
-      'description' => t('This allows the user to submit a nomination at any time, regardless of whether or not nominations are open.'),
-    ),
-    'bypass nomination exclusivity' => array(
-      'title' => t('Bypass nomination exclusivity'),
-      'description' => t('This allows the user to submit a nomination for more than one exclusive position.'),
-    ),
-    'allow duplicate nominations' => array(
-      'title' => t('Allow duplicate nominations'),
-      'description' => t('This allows the user to submit more than one nomination for the same position.'),
-    ),
     'submit nominations' => array(
       'title' => t('Submit nominations'),
       'description' => t('This allows the user to submit a nomination, provided nominations are open.'),
@@ -189,6 +177,7 @@ function election_candidate_access($op, $candidate = NULL, $account = NULL) {
       }
       break;
 
+    case 'create':
     case 'edit':
     case 'update':
     case 'delete':
@@ -228,6 +217,13 @@ function election_candidate_access_per_election($op, stdClass $election, $accoun
 }
 
 /**
+ * Helper function to allow determining candidate access per election post.
+ */
+function election_candidate_access_per_post($op, stdClass $post, $account = NULL) {
+  return election_candidate_access_per_election($op, $post->election, $account);
+}
+
+/**
  * Implements hook_menu().
  */
 function election_candidate_menu() {
@@ -299,6 +295,16 @@ function election_candidate_menu() {
     'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
   );
 
+  $items['election-post/%election_post/add-candidate'] = array(
+    'title' => 'Add new candidate',
+    'page callback' => 'election_candidate_page_add',
+    'page arguments' => array(1),
+    'file' => 'election_candidate.admin.inc',
+    'access callback' => 'election_candidate_access_per_post',
+    'access arguments' => array('create', 1),
+    'type' => MENU_LOCAL_ACTION,
+  );
+
   $items['election-candidate/%election_candidate'] = array(
     'title callback' => 'election_candidate_page_title',
     'title arguments' => array(1),
@@ -330,9 +336,9 @@ function election_candidate_menu() {
 
   $items['election-candidate/%election_candidate/edit'] = array(
     'title' => 'Edit',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('election_candidate_edit_form', 1),
-    'file' => 'election_candidate.forms.inc',
+    'page callback' => 'election_candidate_page_edit',
+    'page arguments' => array(1),
+    'file' => 'election_candidate.admin.inc',
     'access callback' => 'election_candidate_access',
     'access arguments' => array('update', 1),
     'type' => MENU_LOCAL_TASK,
@@ -343,7 +349,7 @@ function election_candidate_menu() {
   $items['election-candidate/%election_candidate/delete'] = array(
     'title' => 'Delete',
     'page callback' => 'drupal_get_form',
-    'page arguments' => array('election_candidate_edit_form_delete_confirm', 1),
+    'page arguments' => array('election_candidate_form_delete_confirm', 1),
     'access callback' => 'election_candidate_access',
     'access arguments' => array('delete', 1),
     'file' => 'election_candidate.forms.inc',
@@ -765,11 +771,11 @@ function election_candidate_nominate_access(stdClass $post, $account = NULL) {
     $prev_post_ids[] = $nomination->post_id;
   }
   // Prevent duplicate nominations for the same post by one account.
-  if (!user_access('allow duplicate nominations', $account) && in_array($post->post_id, $prev_post_ids)) {
+  if (in_array($post->post_id, $prev_post_ids)) {
     return FALSE;
   }
   // Prevent more than one nomination for an 'exclusive' post by one account.
-  if (!empty($post->settings['exclusive']) && $has_exclusive && !user_access('bypass nomination exclusivity', $account)) {
+  if (!empty($post->settings['exclusive']) && $has_exclusive) {
     return FALSE;
   }
   return TRUE;
diff --git a/election_candidate/election_candidate.pages.inc b/election_candidate/election_candidate.pages.inc
index 5fcf9ee..8e3a59a 100644
--- a/election_candidate/election_candidate.pages.inc
+++ b/election_candidate/election_candidate.pages.inc
@@ -45,8 +45,8 @@ function election_candidate_page_view(stdClass $candidate, $view_mode = 'full')
       )),
       PASS_THROUGH
     );
-    $election = election_load($candidate->election_id);
     $post = election_post_load($candidate->post_id);
+    $election = $post->election;
     election_post_set_breadcrumb($election, $post);
   }
 
