diff --git feedback-entry.tpl.php feedback-entry.tpl.php
new file mode 100644
index 0000000..4059a7f
--- /dev/null
+++ feedback-entry.tpl.php
@@ -0,0 +1,30 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Default theme implementation to present a feedback entry.
+ *
+ * @see template_preprocess_feedback_entry()
+ */
+?>
+<div class="feedback-entry"<?php print $attributes; ?>>
+  <div class="field">
+    <div class="field-label"><?php print t('Location'); ?>:&nbsp;</div>
+    <?php print $location; ?>
+  </div>
+  <div class="field">
+    <div class="field-label"><?php print t('Date'); ?>:&nbsp;</div>
+    <?php print $date; ?>
+  </div>
+  <div class="field">
+    <div class="field-label"><?php print t('User'); ?>:&nbsp;</div>
+    <?php print $account; ?>
+  </div>
+  <div class="field">
+    <div class="field-label"><?php print t('Message'); ?>:&nbsp;</div>
+    <?php print $message; ?>
+  </div>
+
+  <?php print render($content); ?>
+</div>
diff --git feedback.admin.inc feedback.admin.inc
index 6256b76..dff926d 100644
--- feedback.admin.inc
+++ feedback.admin.inc
@@ -125,3 +125,105 @@ function feedback_admin_view_form_submit($form, &$form_state) {
   }
 }
 
+/**
+ * Form builder; The general feedback settings form.
+ *
+ * Currently this form is a necessity as a parent for the field local tasks.
+ *
+ * @ingroup forms
+ */
+function feedback_admin_settings_form($form, &$form_state) {
+  drupal_set_message(t('There are no feedback settings yet, but you can configure fields.'));
+  $form = array();
+  return $form;
+}
+
+/**
+ * Generate an array for rendering the given entry.
+ *
+ * @param $entry
+ *   A feedback entry object.
+ * @param $view_mode
+ *   View mode, e.g. 'full'.
+ * @param $langcode
+ *   (optional) A language code to use for rendering. Defaults to the global
+ *   content language of the current request.
+ *
+ * @return
+ *   An array as expected by drupal_render().
+ */
+function feedback_view($entry, $view_mode = 'full', $langcode = NULL) {
+  if (!isset($langcode)) {
+    $langcode = $GLOBALS['language_content']->language;
+  }
+
+  // Retrieve all entry fields and attach to $entry->content.
+  feedback_build_content($entry, $view_mode, $langcode);
+
+  $build = $entry->content;
+  // We don't need duplicate rendering info in entry->content.
+  unset($entry->content);
+
+  $build += array(
+    '#theme' => 'feedback_entry',
+    '#entry' => $entry,
+    '#view_mode' => $view_mode,
+    '#language' => $langcode,
+  );
+
+  // Allow modules to modify the structured entry.
+  $type = 'feedback';
+  drupal_alter(array('feedback_view', 'entity_view'), $build, $type);
+
+  return $build;
+}
+
+/**
+ * Builds a structured array representing the feedback message content.
+ *
+ * @param $entry
+ *   A feedback entry object.
+ * @param $view_mode
+ *   View mode, e.g. 'full'.
+ * @param $langcode
+ *   (optional) A language code to use for rendering. Defaults to the global
+ *   content language of the current request.
+ */
+function feedback_build_content($entry, $view_mode = 'full', $langcode = NULL) {
+  if (!isset($langcode)) {
+    $langcode = $GLOBALS['language_content']->language;
+  }
+
+  // Build fields content.
+  field_attach_prepare_view('feedback', array($entry->fid => $entry), $view_mode);
+  entity_prepare_view('feedback', array($entry->fid => $entry));
+  $entry->content = field_attach_view('feedback', $entry, $view_mode, $langcode);
+
+  module_invoke_all('feedback_view', $entry, $view_mode, $langcode);
+  module_invoke_all('entity_view', $entry, 'feedback', $view_mode, $langcode);
+}
+
+/**
+ * Process variables for feedback-entry.tpl.php.
+ *
+ * The $variables array contains the following arguments:
+ * - $entry
+ *
+ * @see feedback-entry.tpl.php
+ */
+function template_preprocess_feedback_entry(&$variables) {
+  foreach (element_children($variables['elements']) as $key) {
+    $variables['content'][$key] = $variables['elements'][$key];
+  }
+
+  $entry = $variables['elements']['#entry'];
+
+  // Preprocess fields.
+  field_attach_preprocess('feedback', $entry, $variables['elements'], $variables);
+
+  $variables['location'] = l($entry->location, $entry->url);
+  $variables['date'] = format_date($entry->timestamp, 'small');
+  $variables['account'] = format_username($entry);
+  $variables['message'] = feedback_format_message($entry);
+}
+
diff --git feedback.api.php feedback.api.php
index 290307f..824569e 100644
--- feedback.api.php
+++ feedback.api.php
@@ -24,6 +24,19 @@ function hook_feedback_load($entries) {
 }
 
 /**
+ * Act on a feedback entry before it is saved.
+ *
+ * Modules implementing this hook can act on the feedback entry object before it
+ * is inserted or updated.
+ *
+ * @param $entry
+ *   The feedback entry object.
+ */
+function hook_feedback_presave($entry) {
+  $entry->foo = 'bar';
+}
+
+/**
  * Respond to creation of a new feedback entry.
  *
  * @param $entry
@@ -70,5 +83,51 @@ function hook_feedback_delete($entry) {
 }
 
 /**
+ * The feedback entry is being displayed.
+ *
+ * The module should format its custom additions for display and add them to the
+ * $entry->content array.
+ *
+ * @param $entry
+ *   The feedback entry object.
+ * @param $view_mode
+ *   View mode, e.g. 'full'.
+ * @param $langcode
+ *   The language code used for rendering.
+ *
+ * @see hook_feedback_view_alter()
+ * @see hook_entity_view()
+ */
+function hook_feedback_view($entry, $view_mode, $langcode) {
+  $entry->content['foo'] = array(
+    '#markup' => t('Bar'),
+  );
+}
+
+/**
+ * The feedback entry was built; the module may modify the structured content.
+ *
+ * This hook is called after the content has been assembled in a structured
+ * array and may be used for doing processing which requires that the complete
+ * content structure has been built.
+ *
+ * @param $build
+ *   A renderable array representing the feedback entry.
+ *
+ * @see feedback_view()
+ * @see hook_entity_view_alter()
+ */
+function hook_feedback_view_alter(&$build) {
+  // Check for the existence of a field added by another module.
+  if (isset($build['an_additional_field'])) {
+    // Change its weight.
+    $build['an_additional_field']['#weight'] = -10;
+  }
+
+  // Add a #post_render callback to act on the rendered HTML of the entry.
+  $build['#post_render'][] = 'my_module_feedback_post_render';
+}
+
+/**
  * @} End of "addtogroup hooks".
  */
diff --git feedback.controller.inc feedback.controller.inc
new file mode 100644
index 0000000..d10f950
--- /dev/null
+++ feedback.controller.inc
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Entity controller class.
+ */
+
+/**
+ * Controller class for feedback entries.
+ *
+ * This extends the DrupalDefaultEntityController class, adding required
+ * special handling for feedback objects.
+ */
+class FeedbackController extends DrupalDefaultEntityController {
+
+  protected function buildQuery($ids, $conditions = array(), $revision_id = FALSE) {
+    $query = parent::buildQuery($ids, $conditions, $revision_id);
+    // Specify an additional field from the user table.
+    $query->innerJoin('users', 'u', 'base.uid = u.uid');
+    $query->fields('u', array('name'));
+    return $query;
+  }
+}
\ No newline at end of file
diff --git feedback.info feedback.info
index c42773f..db396bb 100644
--- feedback.info
+++ feedback.info
@@ -2,4 +2,6 @@ name = Feedback
 description = Allows site visitors and users to report issues about this site.
 package = Development
 core = 7.x
+configure = admin/config/user-interface/feedback
+files[] = feedback.controller.inc
 files[] = tests/feedback.test
diff --git feedback.module feedback.module
index 65c2acd..c216a20 100644
--- feedback.module
+++ feedback.module
@@ -6,6 +6,66 @@
  */
 
 /**
+ * Implements hook_theme().
+ */
+function feedback_theme() {
+  return array(
+    'feedback_admin_view_form' => array(
+      'render element' => 'form',
+    ),
+    'feedback_entry' => array(
+      'render element' => 'elements',
+      'template' => 'feedback-entry',
+      'file' => 'feedback.admin.inc',
+    ),
+  );
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function feedback_entity_info() {
+  $return = array(
+    'feedback' => array(
+      'label' => t('Feedback'),
+      'controller class' => 'FeedbackController',
+      'base table' => 'feedback',
+      'uri callback' => 'feedback_uri',
+      'fieldable' => TRUE,
+      'entity keys' => array(
+        'id' => 'fid',
+      ),
+      'bundles' => array(
+        'feedback' => array(
+          'label' => t('Feedback'),
+          'admin' => array(
+            'path' => 'admin/config/user-interface/feedback',
+            'access arguments' => array('administer feedback'),
+          ),
+        ),
+      ),
+      'view modes' => array(
+        'full' => array(
+          'label' => t('Full feedback entry'),
+          'custom settings' => FALSE,
+        ),
+      ),
+    ),
+  );
+
+  return $return;
+}
+
+/**
+ * Entity uri callback.
+ */
+function feedback_uri($entry) {
+  return array(
+    'path' => 'admin/reports/feedback/' . $entry->fid,
+  );
+}
+
+/**
  * Implements hook_permission().
  */
 function feedback_permission() {
@@ -18,16 +78,8 @@ function feedback_permission() {
       'title' => t('View feedback messages'),
       'description' => t('View, process, and delete submitted feedback messages.'),
     ),
-  );
-}
-
-/**
- * Implements hook_theme().
- */
-function feedback_theme() {
-  return array(
-    'feedback_admin_view_form' => array(
-      'render element' => 'form',
+    'administer feedback' => array(
+      'title' => t('Administer feedback settings'),
     ),
   );
 }
@@ -44,6 +96,27 @@ function feedback_menu() {
     'access arguments' => array('view feedback messages'),
     'file' => 'feedback.admin.inc',
   );
+  $items['admin/reports/feedback/%feedback'] = array(
+    'title' => 'Feedback entry',
+    'page callback' => 'feedback_view',
+    'page arguments' => array(3),
+    'access arguments' => array('view feedback messages'),
+    'file' => 'feedback.admin.inc',
+  );
+  $items['admin/config/user-interface/feedback'] = array(
+    'title' => 'Feedback',
+    'description' => 'Administer feedback settings.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('feedback_admin_settings_form'),
+    'access arguments' => array('administer feedback'),
+    'file' => 'feedback.admin.inc',
+  );
+  $items['admin/config/user-interface/feedback/settings'] = array(
+    'title' => 'Settings',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+
   return $items;
 }
 
@@ -123,10 +196,10 @@ function feedback_form($form, &$form_state) {
   );
   if (user_access('view feedback messages')) {
     if (arg(0) != 'node') {
-      $feedbacks = feedback_load_multiple(array(), array('f.status' => 0, 'f.location_masked' => feedback_mask_path($_GET['q'])));
+      $feedbacks = feedback_load_multiple(array(), array('status' => 0, 'location_masked' => feedback_mask_path($_GET['q'])));
     }
     else {
-      $feedbacks = feedback_load_multiple(array(), array('f.status' => 0, 'f.location' => $_GET['q']));
+      $feedbacks = feedback_load_multiple(array(), array('status' => 0, 'location' => $_GET['q']));
     }
     if ($feedbacks) {
       $rows = '';
@@ -149,6 +222,10 @@ function feedback_form($form, &$form_state) {
     '#required' => TRUE,
     '#wysiwyg' => FALSE,
   );
+
+  $entry = new stdClass();
+  field_attach_form('feedback', $entry, $form, $form_state);
+
   $form['actions'] = array(
     '#type' => 'actions',
     // Without clearfix, the AJAX throbber wraps in an ugly way.
@@ -177,6 +254,7 @@ function feedback_form($form, &$form_state) {
  */
 function feedback_form_submit($form, &$form_state) {
   $entry = new stdClass();
+  entity_form_submit_build_entity('feedback', $entry, $form, $form_state);
   $entry->message = $form_state['values']['message'];
   $entry->location = $form_state['values']['location'];
   feedback_save($entry);
@@ -241,6 +319,10 @@ function feedback_format_message($entry) {
       $message .= '<div class="browserinfo">(' . check_plain($entry->useragent) . ')</div>';
     }
   }
+  $uri = entity_uri('feedback', $entry);
+  if ($uri['path'] != $_GET['q']) {
+    $message .= l("view full", $uri['path']);
+  }
   return $message;
 }
 
@@ -273,23 +355,14 @@ function feedback_load($fid) {
  *
  * @return
  *   An array of feedback entry objects indexed by fid.
+ *
+ * @see hook_feedback_load()
+ * @see feedback_load()
+ * @see entity_load()
+ * @see EntityFieldQuery
  */
 function feedback_load_multiple($fids = array(), $conditions = array()) {
-  $query = db_select('feedback', 'f')->fields('f');
-  $query->join('users', 'u', 'f.uid = u.uid');
-  $query->fields('u', array('name'));
-
-  if (!empty($fids)) {
-    $query->condition('fid', $fids, 'IN');
-  }
-  if (!empty($conditions)) {
-    foreach ($conditions as $key => $value) {
-      $query->condition($key, $value);
-    }
-  }
-  $entries = $query->execute()->fetchAllAssoc('fid');
-  module_invoke_all('feedback_load', $entries);
-  return $entries;
+  return entity_load('feedback', $fids, $conditions);
 }
 
 /**
@@ -305,30 +378,47 @@ function feedback_load_multiple($fids = array(), $conditions = array()) {
 function feedback_save($entry) {
   global $user;
 
+  // Load the stored entity, if any.
+  if (!empty($entry->fid) && !isset($entry->original)) {
+    $entry->original = entity_load_unchanged('feedback', $entry->fid);
+  }
+
+  field_attach_presave('feedback', $entry);
+
+  // Allow modules to alter the feedback entry before saving.
+  module_invoke_all('feedback_presave', $entry);
+  module_invoke_all('entity_presave', $entry, 'feedback');
+
   if (empty($entry->fid)) {
-    if (!isset($entry->uid)) {
-      $entry->uid = $user->uid;
-    }
     $entry->message = trim($entry->message);
-    if (!isset($entry->location_masked)) {
-      $entry->location_masked = feedback_mask_path($entry->location);
-    }
-    if (!isset($entry->url)) {
-      $entry->url = url($entry->location, array('absolute' => TRUE));
-    }
-    if (!isset($entry->timestamp)) {
-      $entry->timestamp = REQUEST_TIME;
-    }
-    if (!isset($entry->useragent)) {
-      $entry->useragent = $_SERVER['HTTP_USER_AGENT'];
+
+    $defaults = array(
+      'uid' => $user->uid,
+      'location_masked' => feedback_mask_path($entry->location),
+      'url' => url($entry->location, array('absolute' => TRUE)),
+      'timestamp' => REQUEST_TIME,
+      'useragent' => $_SERVER['HTTP_USER_AGENT'],
+    );
+    foreach ($defaults as $key => $default) {
+      if (!isset($entry->$key)) {
+        $entry->$key = $default;
+      }
     }
+
     $status = drupal_write_record('feedback', $entry);
+    field_attach_insert('feedback', $entry);
     module_invoke_all('feedback_insert', $entry);
+    module_invoke_all('entity_insert', $entry, 'feedback');
   }
   else {
     $status = drupal_write_record('feedback', $entry, 'fid');
+
+    field_attach_update('feedback', $entry);
     module_invoke_all('feedback_update', $entry);
+    module_invoke_all('entity_update', $entry, 'feedback');
   }
+  unset($entry->original);
+
   return $status;
 }
 
@@ -352,7 +442,9 @@ function feedback_delete_multiple($fids) {
   if (!empty($fids)) {
     $entries = feedback_load_multiple($fids);
     foreach ($entries as $fid => $entry) {
+      field_attach_delete('feedback', $entry);
       module_invoke_all('feedback_delete', $entry);
+      module_invoke_all('entity_delete', $entry, 'feedback');
     }
     db_delete('feedback')
       ->condition('fid', $fids, 'IN')
