diff --git a/flag.inc b/flag.inc
index c4b1bee..298cb16 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) {
@@ -1167,16 +1175,175 @@ class flag_flag {
 }
 
 /**
+ * Base entity flag handler.
+ */
+class flag_entity extends flag_flag {
+  var $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, if it already exists.
+   */
+  function get_content_id($entity) {
+    if ($entity && isset($entity->{$this->entity_info['entity keys']['id']})) {
+      return $entity->{$this->entity_info['entity keys']['id']};
+    }
+  }
+
+  /**
+   * Returns TRUE if the link should be displayed.
+   */
+  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;
@@ -1198,18 +1365,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'),
@@ -1229,17 +1384,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 access_multiple($content_ids, $account = NULL) {
@@ -1258,10 +1403,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.
    *
@@ -1299,10 +1440,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;
@@ -1319,50 +1456,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,
@@ -1393,17 +1496,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 access_multiple($content_ids, $account = NULL) {
@@ -1466,26 +1559,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' => '',
@@ -1513,6 +1596,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) {
@@ -1522,19 +1606,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 access($content_id, $action = NULL, $account = NULL) {
     $access = parent::access($content_id, $action, $account);
     $account = isset($account) ? $account : $GLOBALS['user'];
@@ -1564,10 +1635,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;
@@ -1575,17 +1642,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);
@@ -1601,15 +1657,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 33aa272..53b6bcd 100644
--- a/flag.module
+++ b/flag.module
@@ -453,28 +453,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().
diff --git a/includes/flag.export.inc b/includes/flag.export.inc
index 26ffc0e..7d0b469 100644
--- a/includes/flag.export.inc
+++ b/includes/flag.export.inc
@@ -51,6 +51,10 @@ function flag_export_flags($flags = array(), $module = '', $indent = '') {
     unset($new_flag['fid']);
     // The name is emitted as the key for the array.
     unset($new_flag['name']);
+    // Do not export the whole entity info, as it is just used as helper data.
+    if (isset($new_flag['entity_info'])) {
+      unset($new_flag['entity_info']);
+    }
 
     $output .= $indent . '// Exported flag: "'. check_plain($flag->get_title()) . '"' . ".\n";
     $output .= $indent . '$flags[\'' . $flag->name . '\'] = ' . (function_exists('features_var_export') ? features_var_export($new_flag, $indent) : var_export($new_flag, TRUE)) . ";\n";
