diff --git a/flag.inc b/flag.inc
index ca27db4..0efed97 100644
--- a/flag.inc
+++ b/flag.inc
@@ -16,26 +16,34 @@
  *   should point to the PHP class implementing this flag.
  */
 function flag_flag_definitions() {
-  return array(
-    'node' => array(
-      'title' => t('Nodes'),
-      'description' => t("Nodes are a Drupal site's primary content."),
-      'handler' => 'flag_node',
-    ),
-    'comment' => array(
-      'title' => t('Comments'),
-      'description' => t('Comments are responses to node content.'),
-      'handler' => 'flag_comment',
-    ),
-    'user' => array(
-      'title' => t('Users'),
-      'description' => t('Users who have created accounts on your site.'),
-      'handler' => 'flag_user',
-    ),
-  );
+  $flags = array();
+  foreach (entity_get_info() as $type => $entity) {
+    // Only add flag support for non-config entities.
+    if (!isset($entity['configuration']) || !$entity['configuration']) {
+      $flags[$type] = array(
+        'title' => $entity['label'],
+        'description' => t('@entity-type entity', array('@entity-type' => $entity['label'])),
+        'handler' => flag_get_handler($type),
+      );
+    }
+  }
+  return $flags;
 }
 
 /**
+ * Returns the flag handler class for an entity type, which is flag_{entity_type}.
+ * If no entity type specific implementation exists, the default flag_entity
+ * class is used.
+ */
+function flag_get_handler($entity_type) {
+  if(class_exists('flag_' . $entity_type)) {
+    return 'flag_' . $entity_type;
+  }
+  return 'flag_entity';
+}
+
+
+/**
  * Returns a flag definition.
  */
 function flag_fetch_definition($content_type = NULL) {
@@ -1239,16 +1247,173 @@ class flag_flag {
 }
 
 /**
+ * Base entity flag handler.
+ */
+class flag_entity extends flag_flag {
+  protected $entity_info;
+  
+  /**
+   * Default contructor that furthermore loads the entity info for the current content_type.
+   * 
+   * content_type is used as synonym for entity_type in this handler.
+   */
+  function construct() {
+    parent::construct();
+    $this->entity_info = entity_get_info($this->content_type);
+  }
+
+  /**
+   * Adds additional options that are common for all entity types.
+   */
+  function options() {
+    $options = parent::options();
+    $options += array(
+      'show_on_entity' => TRUE,
+      'access_author' => '',
+    );
+    return $options;
+  }
+
+  /**
+   * Returns forms for the addional options.
+   */
+  function options_form(&$form) {
+    $bundles = array();
+    foreach ($this->entity_info['bundles'] as $bundle_key => $bundle) {
+      $bundles[$bundle_key] = check_plain($bundle['label']);
+    }
+    $form['access']['types'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Bundles'),
+      '#options' => $bundles,
+      '#description' => t('Check any bundle that this flag may be used on. You must check at least one bundle.'),
+      '#default_value' => $this->types,
+      '#required' => TRUE,
+    );
+    $form['access']['access_author'] = array(
+      '#type' => 'radios',
+      '#title' => t('Flag access by content authorship'),
+      '#options' => array(
+        '' => t('No additional restrictions'),
+        'own' => t('Users may only flag content they own'),
+        'others' => t('Users may only flag content of others'),
+      ),
+      '#default_value' => $this->access_author,
+      '#description' => t("Restrict access to this flag based on the user's ownership of the content. Users must also have access to the flag through the role settings."),
+    );
+    $form['display']['show_on_entity'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Display link on entity'),
+      '#default_value' => isset($this->show_on_entity) ? $this->show_on_entity : TRUE,
+      '#access' => empty($this->locked['show_on_entity']),
+    );
+  }
+
+  /**
+   * Loads the entity object.
+   */
+  function _load_content($content_id) {
+    if(is_numeric($content_id)) {
+      $entity = entity_load($this->content_type, array($content_id));
+      return reset($entity);
+    }
+    return NULL;
+  }
+
+  /**
+   * Checks whether the flag applies for the current entity type.
+   */
+  function applies_to_content_object($entity) {
+    if (empty($this->entity_info['entity keys']['bundle']) || in_array($entity->{$this->entity_info['entity keys']['bundle']}, $this->types)) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Returns the entity id.
+   */
+  function get_content_id($entity) {
+    return $entity->{$this->entity_info['entity keys']['id']};
+  }
+
+  /**
+   * 
+   */
+  function uses_hook_link($teaser) {
+    if ($this->show_on_entity) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Returns token types for the current entity type.
+   */
+  function get_labels_token_types() {
+    // The token type name might be different to the entity type name. If so,
+    // an own flag entity handler can be used for overriding this.
+    return array_merge(array($this->content_type), parent::get_labels_token_types());
+  }
+  
+  /**
+   * Replaces tokens.
+   */
+  function replace_tokens($label, $contexts, $options, $content_id) {
+    if ($content_id && ($entity = $this->fetch_content($content_id))) {
+      $contexts[$this->content_type] = $entity;
+    }
+    return parent::replace_tokens($label, $contexts, $options, $content_id);
+  }
+
+  /**
+   * Returns a 'flag action' object.
+   */
+  function get_flag_action($content_id) {
+    $flag_action = parent::get_flag_action($content_id);
+    $entity = $this->fetch_content($content_id);
+    $flag_action->content_title = entity_label($this->content_type, $entity);
+    $flag_action->content_url = _flag_url($this->content_type . '/' . $this->get_content_id($entity));
+    return $flag_action;
+  }
+
+  /**
+   * Returns objects the action may possible need.
+   */
+  function get_relevant_action_objects($content_id) {
+    return array(
+      $this->content_type => $this->fetch_content($content_id),
+    );
+  }
+
+  /**
+   * Returns information for the Views integration.
+   */
+  function get_views_info() {
+    return array(
+      'views table' => $this->entity_info['base table'],
+      'join field' => $this->entity_info['entity keys']['id'],
+      'title field' => isset($this->entity_info['entity keys']['label']) ? $this->entity_info['entity keys']['label'] : '',
+      'title' => t('@entity_label flag', array('@entity_label' => $this->entity_info['label'])),
+      'help' => t('Limit results to only those entity flagged by a certain flag; Or display information about the flag set on a entity.'),
+      'counter title' => t('@entity_label flag counter', array('@entity_label' => $this->entity_info['label'])),
+      'counter help' => t('Include this to gain access to the flag counter field.'),
+    );
+  }
+}
+
+/**
  * Implements a node flag.
  */
-class flag_node extends flag_flag {
+class flag_node extends flag_entity {
   function options() {
     $options = parent::options();
+    // Use own display settings in the meanwhile.
+    unset($options['show_on_entity']);
     $options += array(
       'show_on_page' => TRUE,
       'show_on_teaser' => TRUE,
       'show_on_form' => FALSE,
-      'access_author' => '',
       'i18n' => 0,
     );
     return $options;
@@ -1270,18 +1435,6 @@ class flag_node extends flag_flag {
       '#weight' => 5,
     );
 
-    $form['access']['access_author'] = array(
-      '#type' => 'radios',
-      '#title' => t('Flag access by content authorship'),
-      '#options' => array(
-        '' => t('No additional restrictions'),
-        'own' => t('Users may only flag content they own'),
-        'others' => t('Users may only flag content of others'),
-      ),
-      '#default_value' => $this->access_author,
-      '#description' => t("Restrict access to this flag based on the user's ownership of the content. Users must also have access to the flag through the role settings."),
-    );
-
     $form['display']['show_on_teaser'] = array(
       '#type' => 'checkbox',
       '#title' => t('Display link on node teaser'),
@@ -1301,17 +1454,7 @@ class flag_node extends flag_flag {
       '#description' => t('If you elect to have a checkbox on the node edit form, you may specify its initial state in the settings form <a href="@content-types-url">for each content type</a>.', array('@content-types-url' => url('admin/structure/types'))),
       '#access' => empty($this->locked['show_on_form']),
     );
-  }
-
-  function _load_content($content_id) {
-    return is_numeric($content_id) ? node_load($content_id) : NULL;
-  }
-
-  function applies_to_content_object($node) {
-    if ($node && in_array($node->type, $this->types)) {
-      return TRUE;
-    }
-    return FALSE;
+    unset($form['display']['show_on_entity']);
   }
 
   function type_access_multiple($content_ids, $account = NULL) {
@@ -1330,10 +1473,6 @@ class flag_node extends flag_flag {
     return $access;
   }
 
-  function get_content_id($node) {
-    return $node->nid;
-  }
-
   /**
    * Adjust the Content ID to find the translation parent if i18n-enabled.
    *
@@ -1371,10 +1510,6 @@ class flag_node extends flag_flag {
     return parent::get_flagging_record($content_id, $uid, $sid);
   }
 
-  function get_labels_token_types() {
-    return array_merge(array('node'), parent::get_labels_token_types());
-  }
-
   function replace_tokens($label, $contexts, $options, $content_id) {
     if (is_numeric($content_id) && ($node = $this->fetch_content($content_id))) {
       $contexts['node'] = $node;
@@ -1391,50 +1526,16 @@ class flag_node extends flag_flag {
     }
     return parent::replace_tokens($label, $contexts, $options, $content_id);
   }
-
-  function get_flag_action($content_id) {
-    $flag_action = parent::get_flag_action($content_id);
-    $node = $this->fetch_content($content_id);
-    $flag_action->content_title = $node->title;
-    $flag_action->content_url = _flag_url('node/' . $node->nid);
-    return $flag_action;
-  }
-
-  function get_valid_actions() {
-    $actions = module_invoke_all('action_info');
-    foreach ($actions as $callback => $action) {
-      if ($action['type'] != 'node' && !in_array('any', $action['triggers'])) {
-        unset($actions[$callback]);
-      }
-    }
-    return $actions;
-  }
-
-  function get_relevant_action_objects($content_id) {
-    return array(
-      'node' => $this->fetch_content($content_id),
-    );
-  }
-
-  function get_views_info() {
-    return array(
-      'views table' => 'node',
-      'join field' => 'nid',
-      'title field' => 'title',
-      'title' => t('Node flag'),
-      'help' => t('Limit results to only those nodes flagged by a certain flag; Or display information about the flag set on a node.'),
-      'counter title' => t('Node flag counter'),
-      'counter help' => t('Include this to gain access to the flag counter field.'),
-    );
-  }
 }
 
 /**
  * Implements a comment flag.
  */
-class flag_comment extends flag_flag {
+class flag_comment extends flag_entity {
   function options() {
     $options = parent::options();
+    // Use own display settings in the meanwhile.
+    unset($options['show_on_entity']);
     $options += array(
       'access_author' => '',
       'show_on_comment' => TRUE,
@@ -1465,17 +1566,7 @@ class flag_comment extends flag_flag {
       '#default_value' => $this->show_on_comment,
       '#access' => empty($this->locked['show_on_comment']),
     );
-  }
-
-  function _load_content($content_id) {
-    return comment_load($content_id);
-  }
-
-  function applies_to_content_object($comment) {
-    if ($comment && ($node = node_load($comment->nid)) && in_array($node->type, $this->types)) {
-      return TRUE;
-    }
-    return FALSE;
+    unset($form['display']['show_on_entity']);
   }
 
   function type_access_multiple($content_ids, $account) {
@@ -1537,26 +1628,16 @@ class flag_comment extends flag_flag {
       'node' => node_load($comment->nid),
     );
   }
-
-  function get_views_info() {
-    return array(
-      'views table' => 'comment',
-      'join field' => 'cid',
-      'title field' => 'subject',
-      'title' => t('Comment flag'),
-      'help' => t('Limit results to only those comments flagged by a certain flag; Or display information about the flag set on a comment.'),
-      'counter title' => t('Comment flag counter'),
-      'counter help' => t('Include this to gain access to the flag counter field.'),
-    );
-  }
 }
 
 /**
  * Implements a user flag.
  */
-class flag_user extends flag_flag {
+class flag_user extends flag_entity {
   function options() {
     $options = parent::options();
+    // Use own display settings in the meanwhile.
+    unset($options['show_on_entity']);
     $options += array(
       'show_on_profile' => TRUE,
       'access_uid' => '',
@@ -1584,6 +1665,7 @@ class flag_user extends flag_flag {
       '#default_value' => $this->show_on_profile,
       '#access' => empty($this->locked['show_on_profile']),
     );
+    unset($form['display']['show_on_entity']);
   }
 
   function form_input($form_values) {
@@ -1593,19 +1675,6 @@ class flag_user extends flag_flag {
     $this->access_uid = empty($form_values['access_uid']) ? 'others' : '';
   }
 
-  function _load_content($content_id) {
-    return user_load($content_id);
-  }
-
-  function applies_to_content_object($user) {
-    // This user flag doesn't currently support subtypes so we return TRUE for
-    // any user.
-    if ($user) {
-      return TRUE;
-    }
-    return FALSE;
-  }
-
   function type_access($content_id, $action, $account) {
     // Prevent users from flagging themselves.
     if ($this->access_uid == 'others' && $content_id == $account->uid) {
@@ -1629,10 +1698,6 @@ class flag_user extends flag_flag {
     return $access;
   }
 
-  function get_content_id($user) {
-    return $user->uid;
-  }
-
   function uses_hook_link($teaser) {
     if ($this->show_on_profile) {
       return TRUE;
@@ -1640,17 +1705,6 @@ class flag_user extends flag_flag {
     return FALSE;
   }
 
-  function get_labels_token_types() {
-    return array_merge(array('user'), parent::get_labels_token_types());
-  }
-
-  function replace_tokens($label, $contexts, $options, $content_id) {
-    if ($content_id && ($user = $this->fetch_content($content_id))) {
-      $contexts['user'] = $user;
-    }
-    return parent::replace_tokens($label, $contexts, $options, $content_id);
-  }
-
   function get_flag_action($content_id) {
     $flag_action = parent::get_flag_action($content_id);
     $user = $this->fetch_content($content_id);
@@ -1666,15 +1720,9 @@ class flag_user extends flag_flag {
   }
 
   function get_views_info() {
-    return array(
-      'views table' => 'users',
-      'join field' => 'uid',
-      'title field' => 'name',
-      'title' => t('User flag'),
-      'help' => t('Limit results to only those users flagged by a certain flag; Or display information about the flag set on a user.'),
-      'counter title' => t('User flag counter'),
-      'counter help' => t('Include this to gain access to the flag counter field.'),
-    );
+    $views_info = parent::get_views_info();
+    $views_info['title field'] = 'name';
+    return $views_info;
   }
 }
 
diff --git a/flag.module b/flag.module
index 957bb12..0017b7d 100644
--- a/flag.module
+++ b/flag.module
@@ -447,28 +447,17 @@ function flag_form_alter(&$form, &$form_state, $form_id) {
 }
 
 /**
- * Implements hook_comment_view().
+ * Implements hook_entity_view().
  */
-function flag_comment_view($comment) {
-  $links = flag_link('comment', $comment);
-  $comment->content['links']['flag'] = array(
+function flag_entity_view($entity, $type, $view_mode, $langcode) {
+  $links = flag_link($type, $entity, $view_mode == 'teaser');
+  $entity->content['links']['flag'] = array(
     '#theme' => 'links',
     '#links' => $links,
     '#attributes' => array('class' => array('links', 'inline')),
   );
 }
 
-/**
- * Implements hook_node_view().
- */
-function flag_node_view($node, $view_mode) {
-  $links = flag_link('node', $node, $view_mode == 'teaser');
-  $node->content['links']['flag'] = array(
-    '#theme' => 'links__node__flag',
-    '#links' => $links,
-    '#attributes' => array('class' => array('links', 'inline')),
-  );
-}
 
 /**
  * Implements hook_node_insert().
