diff --git a/includes/admin.settings.inc b/includes/admin.settings.inc
index 7b15f1a..4bc9cb9 100644
--- a/includes/admin.settings.inc
+++ b/includes/admin.settings.inc
@@ -53,16 +53,17 @@ function signup_settings_form() {
   );
 
   $form['adv_settings']['signup_form_location'] = array(
-    '#title' => t('Location of the signup form'),
+    '#title' => t('Default location of the signup control'),
     '#type' => 'radios',
     '#options' => array(
-      'node' => t('Included on each node'),
-      'tab' => t('Under the %signups tab', array('%signups' => t('Signups'))),
-      'none' => t('Do not display signup form'),
+      'node' => t('Include a form on each node'),
+      'link' => t('Include a toggle link'),
+      'tab' => t('Form under the %signups tab', array('%signups' => t('Signups'))),
+      'none' => t('Do not display any signup control'),
     ),
     '#default_value' => variable_get('signup_form_location', 'node'),
-    '#description' => t('On every signup-enabled node, users with permission to
- sign up can be presented with a form. This setting controls where this form should be displayed: either directly on the node itself, on a separate tab, or not at all.'),
+    '#description' => t('On every signup-enabled node (unless overridden in the content type settings), users with permission to
+ sign up can be presented with a form, a link, or a tag. This setting controls what and where it should be displayed: either directly on the node itself as a form or link, on a separate tab as a form, or not at all.'),
     '#prefix' => '<div class="signup-form-location-radios">',
     '#suffix' => '</div>',
   );
diff --git a/includes/node_output.inc b/includes/node_output.inc
index 759f6ce..f1742ab 100644
--- a/includes/node_output.inc
+++ b/includes/node_output.inc
@@ -95,12 +95,16 @@ function _signup_current_user_signup($node, $type = 'node') {
         if (user_access('sign up for content')) {
           // User has permission to do so, so give them the form.
           module_load_include('inc', 'signup', 'includes/signup_form');
-          $output .= drupal_get_form('signup_form', $node, 'auth', $fieldset);
+          if ($type == 'node') {
+            $output .= drupal_get_form('signup_form', $node, 'auth', $fieldset);
+          }
         }
       }
       else {
         // Already signed up, display their info.
-        $output .= _signup_render_signup_edit_form($signup, $type);
+        if ($type == 'node') {
+          $output .= _signup_render_signup_edit_form($signup, $type);
+        }
       }
     }
   }
diff --git a/includes/signup_form.inc b/includes/signup_form.inc
index ce49d8d..a2d5a56 100644
--- a/includes/signup_form.inc
+++ b/includes/signup_form.inc
@@ -196,3 +196,47 @@ function signup_node_admin_add_user_page($node) {
   return $output;
 }
 
+/**
+ * Page callback for signup toggle link.
+ */
+function signup_link_signup($node) {
+  global $user;
+  // @todo do I need to check user_access('sign up for content') or does _signup_menu_access check that/can?
+  // Check for token.
+  if (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], 'node/' . $node->nid . '/signup' . $user->uid)) {
+    return drupal_access_denied();
+  }
+  $js = FALSE;
+  if (isset($_GET['js']) && intval($_GET['js']) == 1) {
+    $js = TRUE;
+  }
+  // Sign-up or cancel.
+  $signup = db_fetch_object(db_query("SELECT sl.*, n.title, u.name, u.mail FROM {signup_log} sl INNER JOIN {node} n ON sl.nid = n.nid INNER JOIN {users} u ON sl.uid = u.uid WHERE sl.uid = %d AND sl.nid = %d", $user->uid, $node->nid));
+  if (empty($signup)) {
+    // Mock signup_form.
+    $signup_form = array(
+      'nid' => $node->nid,
+      'uid' => $user->uid,
+    );
+    $result = signup_sign_up_user($signup_form, !$js);
+    if ($result !== FALSE) {
+      if (user_access('cancel own signups')) {
+        $message = t('Cancel signup');
+      }
+      else {
+        $message = t('You are signed up.');
+      }
+      
+    }
+  }
+  else {
+    signup_cancel_signup($signup->sid, !$js);
+    $message = t('Sign up');
+  }
+  if ($js) {
+    drupal_json($message);
+    return;
+  }
+  // We weren't invoked via JS so set a message and return to the node.
+  drupal_goto('node/' . $node->nid);
+}
\ No newline at end of file
diff --git a/signup.module b/signup.module
index 00dfce9..53fe3ff 100644
--- a/signup.module
+++ b/signup.module
@@ -406,6 +406,16 @@ function signup_menu() {
     'file' => 'broadcast.inc',
     'file path' => $path,
   );
+  $items['node/%node/signup'] = array(
+    'title' => 'Signup link',
+    'page callback' => 'signup_link_signup',
+    'page arguments' => array(1),
+    'access callback' => '_signup_menu_access',
+    'access arguments' => array(1, 'link'),
+    'type' => MENU_CALLBACK,
+    'file' => 'signup_form.inc',
+    'file path' => $path,
+  );
 
   return $items;
 }
@@ -499,6 +509,10 @@ function _signup_menu_access($node, $menu_type = 'node') {
       $admin = _signup_menu_access($node, 'admin');
       $email = _signup_menu_access($node, 'broadcast');
       return $signup || $list || $admin || $email;
+
+    case 'link':
+      $location = variable_get('signup_form_location_' . $node->type, variable_get('signup_form_location', 'node'));
+      return $location == 'link' ? TRUE : FALSE;
   }
 }
 
@@ -695,6 +709,73 @@ function signup_user($type, &$edit, &$account, $category = NULL) {
   }
 }
 
+    if (user_access('sign up for content')) {
+      $current_signup = '';
+      // If they're logged in and already signed up, show their current
+      // signup info and give them the option to cancel.
+      if ($user->uid) {
+        $signup = db_fetch_object(db_query("SELECT sl.*, n.title, u.name FROM {signup_log} sl INNER JOIN {node} n ON sl.nid = n.nid INNER JOIN {users} u ON sl.uid = u.uid WHERE sl.uid = %d AND sl.nid = %d", $user->uid, $node->nid));
+        if (!empty($signup)) {
+          $current_signup = _signup_render_signup_edit_form($signup, $type);
+        }
+      }
+      $output .= theme('signup_signups_closed', $node, $current_signup);
+    }
+/**
+ * Implementation of hook_link().
+ */
+function signup_link($type, $object, $teaser = FALSE) {
+  if ($type == 'node' && !$teaser && _signup_needs_output($object) && $object->signup_status) {
+    global $user;
+    $node = $object;
+    $info_location = variable_get('signup_form_location_' . $node->type, variable_get('signup_form_location', 'node'));
+    if ($info_location == 'link') {
+      // Does the user have permission to sign up?
+      if (user_access('sign up for content')) {
+        // Is the user already signed up?
+        $signup = db_fetch_object(db_query("SELECT sl.*, n.title, u.name FROM {signup_log} sl INNER JOIN {node} n ON sl.nid = n.nid INNER JOIN {users} u ON sl.uid = u.uid WHERE sl.uid = %d AND sl.nid = %d", $user->uid, $node->nid));
+        if (!empty($signup->sid)) {
+          // Does the user have permission to cancel?
+          if (user_access('cancel own signups')) {
+            $path = drupal_get_path('module', 'signup');
+            drupal_add_js("$path/js/signup_link.js");
+            // Generate token.
+            $url = 'node/' . $node->nid . '/signup';
+            $token = drupal_get_token($url . $user->uid);
+            $links['signup_cancel'] = array(
+              'title' => t('Cancel signup'),
+              'href' => $url,
+              'attributes' => array('class' => 'signup-link'),
+              'query' => "token=$token",
+            );
+          }
+          else {
+            $links['signup_signed_up'] = array(
+              'title' => t('You are signed up'),
+              'attributes' => array('class' => 'signup-link'),
+              'html' => TRUE,
+            );
+          }
+        }
+        // The user is not already signed up.
+        else {
+          $path = drupal_get_path('module', 'signup');
+          drupal_add_js("$path/js/signup_link.js");
+          // Generate token.
+          $url = 'node/' . $node->nid . '/signup';
+          $token = drupal_get_token($url . $user->uid);
+          $links['signup'] = array(
+            'title' => t('Sign up'),
+            'href' => $url,
+            'attributes' => array('class' => 'signup-link'),
+            'query' => "token=$token",
+          );
+        }
+      }
+    }
+  }
+  return $links;
+}
 /**
  * Implementation of hook_form_alter().
  *
@@ -734,6 +815,21 @@ function signup_form_node_type_form_alter(&$form, &$form_state) {
     '#default_value' => variable_get('signup_node_default_state_'. $type, 'disabled'),
     '#description' => t('If %disabled is selected, signups will not be possible for this content type. If %allowed_off is selected, signups will be off by default, but users with the %admin_all_signups permission will be able to allow signups for specific posts of this content type. If %enabled_on is selected, users will be allowed to signup for this content type unless an administrator disbles signups on specific posts.', array('%disabled' => t('Disabled'), '%allowed_off' => t('Allowed (off by default)'), '%enabled_on' => t('Enabled (on by default)'), '%admin_all_signups' => t('administer all signups'))),
   );
+  $form['signup']['signup_form_location'] = array(
+    '#title' => t('Location of the signup control'),
+    '#type' => 'radios',
+    '#options' => array(
+      'node' => t('Include a form on each node'),
+      'link' => t('Include a toggle link'),
+      'tab' => t('Form under the %signups tab', array('%signups' => t('Signups'))),
+      'none' => t('Do not display any signup control'),
+    ),
+    '#default_value' => variable_get('signup_form_location_' . $type, variable_get('signup_form_location', 'node')),
+    '#description' => t('On every signup-enabled node, users with permission to
+ sign up can be presented with a form, a link, or a tag. This setting controls what and where it should be displayed: either directly on the node itself as a form or link, on a separate tab as a form, or not at all.'),
+    '#prefix' => '<div class="signup-form-location-radios">',
+    '#suffix' => '</div>',
+  );
 
   if (!empty($type) && function_exists('_signup_date_alter_node_type_form')) {
     _signup_date_alter_node_type_form($form, $form_state);
@@ -796,13 +892,13 @@ function signup_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
       // Only include any of this if we're trying to view the node as
       // a page, not during the view from comment validation, etc.
       if ($page && _signup_needs_output($node)) {
-        $info_location = variable_get('signup_form_location', 'node');
+        $info_location = variable_get('signup_form_location_' . $node->type, variable_get('signup_form_location', 'node'));
         $list_location = variable_get('signup_display_signup_user_list', 'embed-view');
         if ($info_location == 'node' || $list_location == 'embed-view') {
           module_load_include('inc', 'signup', 'includes/node_output');
         }
         if ($info_location == 'node') {
-          $signup_info = _signup_node_output($node);
+          $signup_info = _signup_node_output($node, $info_location);
           if (!empty($signup_info)) {
             if (module_exists('content')) {
               // Due to a bug in CCK (http://drupal.org/node/363456), we need
diff --git a/theme/node.inc b/theme/node.inc
index 1c3c4b9..d2c939e 100644
--- a/theme/node.inc
+++ b/theme/node.inc
@@ -58,6 +58,22 @@ function theme_signup_node_output_header($node) {
   return '<a name="signup"></a>';
 }
 
+/**
+ * Return HTML for the dynamic signup link.
+ *
+ * @param $node
+ *   The fully loaded node object.
+ * @param $signup_url
+ *   Signup link URL with token.
+ * @param $signup_text
+ *   Text to appear in link.
+ *
+ * @return
+ *   HTML to display.
+ *
+ */
+
+
 /*
  * Return HTML desired when displaying a node title along with the signup date.
  */
diff --git a/views/signup.views.inc b/views/signup.views.inc
index e0d5032..21392bd 100644
--- a/views/signup.views.inc
+++ b/views/signup.views.inc
@@ -302,6 +302,14 @@ function signup_views_data() {
     ),
   );
 
+  $data['signup_log']['signup_link'] = array(
+    'title' => t('User: Signup link'),
+    'field' => array(
+      'handler' => 'signup_handler_field_signup_link',
+      'help' => t('Toggle link to signup or cancel signup.'),
+    ),
+  );
+
   return $data;
 }
 
@@ -323,6 +331,9 @@ function signup_views_handlers() {
       'signup_handler_field_signup_edit_link' => array(
         'parent' => 'views_handler_field_node_link',
       ),
+      'signup_handler_field_signup_link' => array(
+        'parent' => 'views_handler_field_node_link',
+      ),
       'signup_handler_field_signup_node_link' => array(
         'parent' => 'views_handler_field_node_link',
       ),
