diff --git a/file_entity.admin.inc b/file_entity.admin.inc
index aa81e20..4b40f93 100644
--- a/file_entity.admin.inc
+++ b/file_entity.admin.inc
@@ -49,7 +49,7 @@ function file_entity_admin_files($form, &$form_state) {
   // Build the sortable table header.
   $header = array(
     'title' => array('data' => t('Title'), 'field' => 'f.filename'),
-    'type' => array('data' => t('Type'), 'field' => 'f.filemime'),
+    'type' => array('data' => t('Type'), 'field' => 'f.type'),
     'size' => array('data' => t('Size'), 'field' => 'f.filesize'),
     'author' => array('data' => t('Author'), 'field' => 'u.name'),
     'timestamp' => array('data' => t('Updated'), 'field' => 'f.timestamp', 'sort' => 'desc'),
@@ -83,9 +83,10 @@ function file_entity_admin_files($form, &$form_state) {
   $destination = drupal_get_destination();
   $options = array();
   foreach ($files as $file) {
+    $file_type = file_type_load($file->type);
     $options[$file->fid] = array(
       'title' => theme('file_entity_file_link', array('file' => $file)),
-      'type' =>  check_plain($file->filemime),
+      'type' => $file_type ? check_plain($file_type->label) : FILE_TYPE_NONE,
       'size' => format_size($file->filesize),
       'author' => theme('username', array('account' => $accounts[$file->uid])),
       'timestamp' => format_date($file->timestamp, 'short'),
@@ -172,23 +173,25 @@ function file_entity_admin_files_submit($form, &$form_state) {
  * Displays the file type admin overview page.
  */
 function file_entity_list_types_page() {
-  $types = file_info_file_types();
-  $entity_info = entity_get_info('file');
+  $file_entity_info = entity_get_info('file');
   $field_ui = module_exists('field_ui');
   $header = array(
     array('data' => t('Name')),
-    array('data' => t('Operations'), 'colspan' => $field_ui ? '3' : '1'),
+    array('data' => t('Operations'), 'colspan' => $field_ui ? '4' : '2'),
   );
   $rows = array();
 
-  foreach ($types as $type => $info) {
-    $row = array(array('data' => theme('file_entity_file_type_overview', $info)));
-    $path = isset($entity_info['bundles'][$type]['admin']['real path']) ? $entity_info['bundles'][$type]['admin']['real path'] : NULL;
+  foreach (file_type_get_all_types(TRUE) as $type) {
+    $row = array(array('data' => theme('file_entity_file_type_overview', array('label' => $type->label, 'description' => $type->description))));
+    $path = isset($file_entity_info['bundles'][$type->type]['admin']['real path']) ? $file_entity_info['bundles'][$type->type]['admin']['real path'] : NULL;
+
+    $row[] = array('data' => isset($path) ? l(t('edit file type'), $path . '/edit') : '');
     if ($field_ui) {
       $row[] = array('data' => isset($path) ? l(t('manage fields'), $path . '/fields') : '');
       $row[] = array('data' => isset($path) ? l(t('manage display'), $path . '/display') : '');
     }
     $row[] = array('data' => isset($path) ? l(t('manage file display'), $path . '/file-display') : '');
+
     $rows[] = $row;
   }
 
@@ -206,6 +209,8 @@ function file_entity_list_types_page() {
  * Form callback; presents file display settings for a given view mode.
  */
 function file_entity_file_display_form($form, &$form_state, $file_type, $view_mode) {
+  $file_type = $file_type->type;
+
   $form['#file_type'] = $file_type;
   $form['#view_mode'] = $view_mode;
   $form['#tree'] = TRUE;
@@ -345,3 +350,109 @@ function theme_file_entity_file_display_order($variables) {
 
   return $output;
 }
+
+/**
+ * Admin screen for file type settings.
+ */
+function file_entity_file_type_form($form, &$form_state, $type) {
+  $form['#file_type'] = $type->type;
+
+  $form['label'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Name'),
+    '#description' => t('This is the human readable name of the file type.'),
+    '#required' => TRUE,
+    '#default_value' => $type->label,
+  );
+  $form['description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Description'),
+    '#description' => t('This is the description of the file type.'),
+    '#default_value' => $type->description,
+  );
+
+  $form['mimetypes'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Mimetypes'),
+    '#description' => t('Enter one mimetype per line.'),
+    '#default_value' => implode("\n", $type->mimetypes),
+  );
+
+  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
+  $mimetypes = file_mimetype_mapping();
+
+  $form['mimetype_mapping'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Mimetype List'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['mimetype_mapping']['mapping'] = array(
+    '#theme' => 'item_list',
+    '#items' => $mimetypes['mimetypes'],
+  );
+
+  // Options for allowed Streams.
+  $options = array('public' => t('Public files'), 'private' => t('Private files'));
+  foreach (file_get_stream_wrappers() as $stream => $wrapper) {
+    $options[$stream] = $wrapper['name'];
+  }
+  unset($options['temporary']);
+  $default_value = array();
+  if (isset($type->streams)) {
+    foreach ($type->streams as $stream) {
+      $default_value[$stream] = $stream;
+    }
+  }
+  $form['streams'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Allowed streams'),
+    '#options' => $options,
+    '#default_value' => $default_value,
+  );
+
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validation handler for file type settings form.
+ */
+function file_entity_file_type_form_validate($form, &$form_state) {
+  include_once DRUPAL_ROOT . '/includes/file.mimetypes.inc';
+  $mimetype_mapping = file_mimetype_mapping();
+
+  $valid_mimetypes = $mimetype_mapping['mimetypes'];
+  $submitted_mimetypes = array_filter(array_map('trim', explode("\n", $form_state['values']['mimetypes'])));
+
+  $invalid_mimetypes = array_diff($submitted_mimetypes, $valid_mimetypes);
+  foreach ($invalid_mimetypes as $mimetype) {
+    form_set_error('mimetypes', t('The mimetype %mimetype is not a valid mimetype.', array('%mimetype' => $mimetype)));
+  }
+}
+
+/**
+ * Submit handler for file type settings form.
+ */
+function file_entity_file_type_form_submit($form, &$form_state) {
+  $type = file_type_load($form['#file_type']);
+
+  $type->label = $form_state['values']['label'];
+  $type->description = $form_state['values']['description'];
+  $type->mimetypes = array_filter(array_map('trim', explode("\n", $form_state['values']['mimetypes'])));
+  $type->streams = array();
+  foreach ($form_state['values']['streams'] as $stream) {
+    if ($stream) {
+      $type->streams[] = $stream;
+    }
+  }
+
+  file_type_save($type);
+
+  drupal_set_message(t('The file type %type has been updated.', array('%type' => $type->label)));
+  $form_state['redirect'] = 'admin/structure/file-types';
+}
diff --git a/file_entity.api.php b/file_entity.api.php
index 99aa893..5094365 100644
--- a/file_entity.api.php
+++ b/file_entity.api.php
@@ -6,61 +6,44 @@
  */
 
 /**
- * Define file types.
+ * Defines default file types.
+ *
+ * File types are implemented as CTools exportables, so modules can alter the
+ * defaults via hook_file_default_types_alter(), and the administrator can
+ * save overridden and custom types to the {file_type} database table.
  *
  * @return
  *   An array whose keys are file type names and whose values are arrays
  *   describing the file type, with the following key/value pairs:
+ *   - api_version: The version of this data.
+ *   - type: The file type name.
  *   - label: The human-readable name of the file type.
- *   - default view callback: (optional) The name of the function that returns a
- *     drupal_render() array for displaying the file. Used when there are no
- *     administrator configured file formatters, or none of the configured ones
- *     return a display. See hook_file_type_TYPE_default_view() for details.
- *   - description: (optional) A short description of the file type.
- *   - weight: (optional) A number defining the order in which the 'claim
- *     callback' function for this type is called relative to the claim
- *     callbacks of other defined types, when the type of a file needs to be
- *     determined. The type with the lowest weighted claim callback to return
- *     TRUE is assigned to the file. Also, on administrative pages listing file
- *     types, the types are ordered by weight.
- *   - admin: (optional) An array of information, to be added to the
- *     ['bundles'][TYPE]['admin'] entry for the 'file' entity type, thereby
- *     controlling the path at which Field UI pages are attached for this file
- *     type, and which users may access them. Defaults to attaching the Field UI
- *     pages to the admin/structure/file-types/manage/TYPE path and requiring
- *     'administer site configuration' permission. See hook_entity_info() for
- *     details about this array. This value can also be set to NULL to suppress
- *     Field UI pages from attaching at all for this file type.
- *
- * @see hook_file_type_info_alter()
+ *   - description: The description of this file type.
+ *   - mimetpes: An array of mimetypes that this file type will map to.
  */
-function hook_file_type_info() {
+function hook_file_default_types() {
   return array(
-    'image' => array(
+    'image' => (object) array(
+      'api_version' => 1,
+      'type' => 'image',
       'label' => t('Image'),
+      'description' => t("An <em>Image</em> is a two-dimensional picture that has a similar appearance to some subject, usually a physical object or a person."),
+      'mimetypes' => array(
+        'image/jpeg',
+        'image/gif',
+        'image/png',
+      ),
     ),
   );
 }
 
 /**
- * Perform alterations on file types.
+ * Performs alterations on default file types.
  *
- * @param $info
- *   Array of information on file types exposed by hook_file_type_info()
- *   implementations.
+ * @see hook_file_default_types()
  */
-function hook_file_type_info_alter(&$info) {
-  // @todo Add example.
-}
-
-/**
- * @todo Add documentation.
- *
- * Note: This is not really a hook. The function name is manually specified via
- * 'default view callback' in hook_file_type_info(), with this recommended
- * callback name pattern.
- */
-function hook_file_type_TYPE_default_view($file, $view_mode, $langcode) {
+function hook_file_default_types_alter(&$types) {
+  $types['image']->mimetypes[] = 'image/svg+xml';
 }
 
 /**
@@ -171,3 +154,32 @@ function hook_file_operation_info_alter(&$info) {
   // Remove the 'Fluff selected files' operation.
   unset($info['fluff']);
 }
+
+/**
+ * Decides which file type (bundle) should be assigned to 
+ * a file entity.
+ * 
+ * @param $file File object.
+ * @return Array of file type machine names that can be assigned to a given
+ *   file type. If there are more proposed file types the one, that was returned
+ *   the first, wil be chosen. This can be, however, changed in alter hook.
+ *   
+ * @see hook_file_type_alter()
+ */
+function hook_file_type($file) {
+  // Assign all files uploaded by anonymous users to a special file type.
+  if (user_is_anonymous()) {
+    return array('untrusted_files');
+  }
+}
+
+/**
+ * Alters list of file types that can be assigned to a file.
+ * 
+ * @param $types List of proposed types.
+ * @param $file File object.
+ */
+function hook_file_type_alter(&$types, $file) {
+  // Choose a specific, non-first, file type.
+  $types = array($types[4]);
+}
\ No newline at end of file
diff --git a/file_entity.file.inc b/file_entity.file.inc
index 3b76626..025ddf7 100644
--- a/file_entity.file.inc
+++ b/file_entity.file.inc
@@ -14,13 +14,40 @@ function file_entity_file_presave($file) {
     $file->filemime = file_get_mimetype($file->uri);
   }
 
-  // Always update file type based on filemime.
-  $file->type = file_get_type($file);
+  // The file type is used as a bundle key, and therefore, must not be NULL.
+  // It defaults to FILE_TYPE_NONE when loaded via file_load(), but in case
+  // file_save() is called on a new file object, default it here too.
+  if (!isset($file->type)) {
+    $file->type = FILE_TYPE_NONE;
+  }
+
+  // If the file isn't already assigned a real type, determine what type should
+  // be assigned to it.
+  if ($file->type === FILE_TYPE_NONE) {
+    $type = file_get_type($file);
+    if (isset($type)) {
+      $file->type = $type;
+    }
+  }
 
   field_attach_presave('file', $file);
 }
 
 /**
+ * Implements hook_file_type().
+ */
+function file_entity_file_type($file) {
+  $types = array();
+  foreach (file_type_get_enabled_types() as $type) {
+    if (in_array($file->filemime, $type->mimetypes)) {
+      $types[] = $type->type;
+    }
+  }
+  
+  return $types;
+}
+
+/**
  * Implements hook_file_insert().
  */
 function file_entity_file_insert($file) {
diff --git a/file_entity.file_api.inc b/file_entity.file_api.inc
index 339d91f..349eb99 100644
--- a/file_entity.file_api.inc
+++ b/file_entity.file_api.inc
@@ -6,59 +6,9 @@
  */
 
 /**
- * Returns information about file types from hook_file_type_info().
- *
- * @param $file_type
- *   (optional) A file type name. If ommitted, all file types will be returned.
- *
- * @return
- *   Either a file type description, as provided by hook_file_type_info(), or an
- *   array of all existing file types, keyed by file type name.
+ * The {file_managed}.type value when the file type has not yet been determined.
  */
-function file_info_file_types($file_type = NULL) {
-  $info = &drupal_static(__FUNCTION__);
-  if (!isset($info)) {
-    $info = module_invoke_all('file_type_info');
-
-    // Add support for the standard file types until this can be fully
-    // abstracted out of Media module.
-    $info += array(
-      'application' => array('label' => t('Application (multipurpose)')),
-      'audio' => array('label' => t('Audio')),
-      'image' => array('label' => t('Image')),
-      'text' => array('label' => t('Text')),
-      'video' => array('label' => t('Video')),
-    );
-
-    drupal_alter('file_type_info', $info);
-    uasort($info, '_file_entity_sort_weight_label');
-  }
-  if ($file_type) {
-    if (isset($info[$file_type])) {
-      return $info[$file_type];
-    }
-  }
-  else {
-    return $info;
-  }
-}
-
-/**
- * Determines the file type of a passed in file object.
- *
- * The file type is determined by extracting the 'first' part of the file's
- * MIME type. For example, a PNG image with a MIME type of 'image/png' will
- * have a file type of 'image'.
- *
- * @link http://www.iana.org/assignments/media-types/index.html IANA list of official MIME media types @endlink
- */
-function file_get_type($file) {
-  // Ensure that a MIME type has been determined first.
-  if (empty($file->filemime)) {
-    $file->filemime = file_get_mimetype($file->uri);
-  }
-  return substr($file->filemime, 0, strpos($file->filemime, '/'));
-}
+define('FILE_TYPE_NONE', 'undefined');
 
 /**
  * Returns information about file formatters from hook_file_formatter_info().
@@ -90,10 +40,15 @@ function file_info_formatter_types($formatter_type = NULL) {
 }
 
 /**
- * Clears the file info cache.
+ * Clears all cached configuration related to file types, formatters, and display settings.
  */
 function file_info_cache_clear() {
-  drupal_static_reset('file_info_file_types');
+  // Clear the CTools managed caches.
+  ctools_include('export');
+  ctools_export_load_object_reset('file_type');
+  ctools_export_load_object_reset('file_display');
+
+  // Clear the formatter type cache, managed by file_info_formatter_types().
   drupal_static_reset('file_info_formatter_types');
 }
 
@@ -299,17 +254,8 @@ function file_view_file($file, $displays = 'full', $langcode = NULL) {
     }
   }
 
-  // If none of the configured formatters were able to display the file, attempt
-  // to display the file using the file type's default view callback.
-  if (!isset($element)) {
-    $file_type_info = file_info_file_types($file->type);
-    if (isset($file_type_info['default view callback']) && ($function = $file_type_info['default view callback']) && function_exists($function)) {
-      $element = $function($file, $view_mode, $langcode);
-    }
-  }
-
-  // If a render element was returned by a formatter or the file type's default
-  // view callback, add some defaults to it and return it.
+  // If a render element was returned by a formatter, add some defaults to it
+  // and return it.
   if (isset($element)) {
     $element += array(
       '#file' => $file,
@@ -321,6 +267,12 @@ function file_view_file($file, $displays = 'full', $langcode = NULL) {
 }
 
 /**
+ * @defgroup file_displays File displays API
+ * @{
+ * Functions to load and save information about file displays.
+ */
+
+/**
  * Returns an array of possible displays to use for a file type in a given view mode.
  *
  * It is common for a site to be configured with broadly defined file types
@@ -414,6 +366,7 @@ function file_displays_load($file_type, $view_mode, $key_by_formatter_name = FAL
 function file_display_save($display) {
   ctools_include('export');
   ctools_export_crud_save('file_display', $display);
+  file_info_cache_clear();
 }
 
 /**
@@ -422,11 +375,237 @@ function file_display_save($display) {
 function file_display_new($file_type, $view_mode, $formatter_name) {
   ctools_include('export');
   $display = ctools_export_crud_new('file_display');
+  file_info_cache_clear();
   $display->name = implode('__', array($file_type, $view_mode, $formatter_name));
   return $display;
 }
 
 /**
+ * @} End of "defgroup file_displays".
+ */
+
+/**
+ * @defgroup file_types File types API
+ * @{
+ * Functions to load and save information about file types.
+ */
+
+/**
+ * Returns an array of all available file types, including enabled and disabled ones.
+ *
+ * @see file_type_get_enabled_types()
+ * @see file_type_get_disabled_types()
+ */
+function file_type_get_all_types($reset = FALSE) {
+  ctools_include('export');
+  return ctools_export_crud_load_all('file_type', $reset);
+}
+
+/**
+ * Returns an array of enabled file types.
+ */
+function file_type_get_enabled_types() {
+  $types = file_type_get_all_types();
+  return array_filter($types, 'file_type_is_enabled');
+}
+
+/**
+ * Returns an array of disabled file types.
+ */
+function file_type_get_disabled_types($reset = FALSE) {
+  $types = file_type_get_all_types();
+  return array_filter($types, 'file_type_is_disabled');
+}
+
+/**
+ * Returns TRUE if a file type is enabled, FALSE otherwise.
+ */
+function file_type_is_enabled($type) {
+  return empty($type->disabled);
+}
+
+/**
+ * Returns TRUE if a file type is disabled, FALSE otherwise.
+ */
+function file_type_is_disabled($view) {
+  return !empty($type->disabled);
+}
+
+/**
+ * Returns the configuration object for the passed in file type name, or FALSE if it doesn't exist.
+ */
+function file_type_load($type_name) {
+  ctools_include('export');
+  $type = ctools_export_crud_load('file_type', $type_name);
+  return isset($type) ? $type : FALSE;
+}
+
+/**
+ * CTools exportables 'subrecords callback' to load {file_type} subrecords.
+ */
+function file_type_load_subrecords($types) {
+  foreach ($types as $type) {
+    $type->mimetypes = db_query('SELECT mimetype FROM {file_type_mimetypes} WHERE type = :type', array(':type' => $type->type))->fetchCol();
+    $type->streams = db_query('SELECT stream FROM {file_type_streams} WHERE type = :type', array(':type' => $type->type))->fetchCol();
+  }
+}
+
+/**
+ * Updates an existing file type or creates a new one.
+ *
+ * This function can be called on its own, or via the CTools exportables
+ * 'save callback' for {file_type} objects.
+ */
+function file_type_save($type) {
+  // Get the old type object, so we now can issue the correct insert/update
+  // queries.
+  if (!empty($type->old_type) && $type->old_type != $type->type) {
+    $rename_bundle = TRUE;
+    $old_type = file_type_load($type->old_type);
+  }
+  else {
+    $rename_bundle = FALSE;
+    $old_type = file_type_load($type->type);
+  }
+
+  // The type and label fields are required, but description is optional.
+  if (!isset($type->description)) {
+    $type->description = '';
+  }
+  $fields = array(
+    'type' => $type->type,
+    'label' => $type->label,
+    'description' => $type->description,
+  );
+
+  // Prepare the mimetype multiple insert query, but don't execute it until
+  // later in this function.
+  if (!empty($type->mimetypes)) {
+    $mimetype_insert = db_insert('file_type_mimetypes')->fields(array('type', 'mimetype'));
+    foreach ($type->mimetypes as $mimetype) {
+      $mimetype_insert->values(array('type' => $type->type, 'mimetype' => $mimetype));
+    }
+  }
+
+  // Prepare the stream multiple insert query, but don't execute it until
+  // later in this function.
+  if (!empty($type->streams)) {
+    $stream_insert = db_insert('file_type_streams')->fields(array('type', 'stream'));
+    foreach ($type->streams as $stream) {
+      $stream_insert->values(array('type' => $type->type, 'stream' => $stream));
+    }
+  }
+
+  // Update an existing type object, whether with a modified 'type' property or
+  // not.
+  if ($old_type) {
+    if ($old_type->export_type & EXPORT_IN_DATABASE) {
+      db_update('file_type')
+        ->fields($fields)
+        ->condition('type', $old_type->type)
+        ->execute();
+      db_delete('file_type_mimetypes')
+        ->condition('type', $old_type->type)
+        ->execute();
+      db_delete('file_type_streams')
+        ->condition('type', $old_type->type)
+        ->execute();
+    }
+    else {
+      db_insert('file_type')
+        ->fields($fields)
+        ->execute();
+    }
+    if (isset($mimetype_insert)) {
+      $mimetype_insert->execute();
+    }
+    if (isset($stream_insert)) {
+      $stream_insert->execute();
+    }
+    if ($rename_bundle) {
+      field_attach_rename_bundle('file', $old_type->type, $type->type);
+    }
+    module_invoke_all('file_type_update', $type);
+    $status = SAVED_UPDATED;
+  }
+  // Insert a new type object.
+  else {
+    db_insert('file_type')
+      ->fields($fields)
+      ->execute();
+    if (isset($mimetype_insert)) {
+      $mimetype_insert->execute();
+    }
+    if (isset($stream_insert)) {
+      $stream_insert->execute();
+    }
+    field_attach_create_bundle('file', $type->type);
+    module_invoke_all('file_type_insert', $type);
+    $status = SAVED_NEW;
+  }
+
+  // Clear the necessary caches.
+  file_info_cache_clear();
+
+  // Ensure the type has the correct export_type in case the $type parameter
+  // continues to be used by the calling function after this function completes.
+  if (empty($type->export_type)) {
+    $type->export_type = 0;
+  }
+  $type->export_type |= EXPORT_IN_DATABASE;
+
+  return $status;
+}
+
+/**
+ * Deletes a file type from the database.
+ *
+ * This function can be called on its own, or via the CTools exportables
+ * 'delete callback' for {file_type} objects.
+ *
+ * @param $type
+ *   Either a loaded file type object or the machine-name of the type.
+ */
+function file_type_delete($type) {
+  $type = is_string($type) ? file_type_load($type) : $type;
+
+  db_delete('file_type')
+    ->condition('type', $type->type)
+    ->execute();
+  db_delete('file_type_mimetypes')
+    ->condition('type', $type->type)
+    ->execute();
+
+  file_info_cache_clear();
+
+  // After deleting from the database, check if the type still exists as a
+  // code-provided default type. If not, consider the type fully deleted and
+  // invoke the needed hooks.
+  if (!file_type_load($type->type)) {
+    field_attach_delete_bundle('file', $type->type);
+    module_invoke_all('file_type_delete', $type);
+  }
+}
+
+/**
+ * Determines file type for a given file.
+ * 
+ * @param $file File object.
+ * @return Machine name of file type that should be used for
+ *   given file.
+ */
+function file_get_type($file) {
+  $types = module_invoke_all('file_type', $file);
+  drupal_alter('file_type', $types, $file);
+  
+  return empty($types) ? NULL : reset($types);
+}
+
+/**
+ * @} End of "defgroup file_types".
+ */
+
+/**
  * Helper function to sort an array by the value of each item's 'weight' key, while preserving relative order of items that have equal weight.
  */
 function _file_sort_array_by_weight(&$a) {
diff --git a/file_entity.install b/file_entity.install
index 305b80b..9a057a8 100644
--- a/file_entity.install
+++ b/file_entity.install
@@ -9,6 +9,97 @@
  * Implements hook_schema().
  */
 function file_entity_schema() {
+  $schema['file_type'] = array(
+    'description' => 'Stores the settings for file types.',
+    'fields' => array(
+      'type' => array(
+        'description' => 'The machine name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'label' => array(
+        'description' => 'The human readable name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'translatable' => TRUE,
+      ),
+      'description' => array(
+        'description' => 'A brief description of this file type.',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'medium',
+        'translatable' => TRUE,
+      ),
+    ),
+    'primary key' => array('type'),
+    'export' => array(
+      'key' => 'type',
+      'key name' => 'Type',
+      'primary key' => 'type',
+      'default hook' => 'file_default_types',
+      'identifier' => 'file_type',
+      'export type string' => 'ctools_type',
+      'subrecords callback' => 'file_type_load_subrecords',
+      'save callback' => 'file_type_save',
+      'delete callback' => 'file_type_delete',
+      'api' => array(
+        'owner' => 'file_entity',
+        'api' => 'file_type',
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
+  );
+  $schema['file_type_mimetypes'] = array(
+    'description' => 'Maps mimetypes to file types.',
+    'fields' => array(
+      'type' => array(
+        'description' => 'The machine name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'mimetype' => array(
+        'description' => 'Mimetypes mapped to this file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      array('file_type' => 'type'),
+      array('file_type_mimetype' => 'mimetype'),
+    ),
+  );
+  $schema['file_type_streams'] = array(
+    'description' => 'Maps mimetypes to file types.',
+    'fields' => array(
+      'type' => array(
+        'description' => 'The machine name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'stream' => array(
+        'description' => 'Streams mapped to this file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      array('file_type' => 'type'),
+      array('file_type_stream' => 'stream'),
+    ),
+  );
   $schema['file_display'] = array(
     'description' => 'Stores configuration options for file displays.',
     'fields' => array(
@@ -107,7 +198,11 @@ function file_entity_schema_alter(&$schema) {
     'type' => 'varchar',
     'length' => 50,
     'not null' => TRUE,
-    'default' => '',
+    // If the FILE_TYPE_NONE constant ever changes, then change the value here
+    // too, and add an update function to deal with existing records. The
+    // constant isn't used here, because there may be cases where this function
+    // runs without the module file loaded.
+    'default' => 'undefined',
   );
   $schema['file_managed']['indexes']['file_type'] = array('type');
 }
@@ -140,12 +235,6 @@ function file_entity_install() {
     db_add_field('file_managed', 'type', $spec, $indexes_new);
   }
 
-  // Update all files with empty types to use the first part of filemime.
-  db_update('file_managed')
-    ->expression('type', "SUBSTRING_INDEX(filemime, '/', 1)")
-    ->condition('type', '')
-    ->execute();
-
   // Set permissions.
   $roles = user_roles();
   foreach ($roles as $rid => $role) {
@@ -157,6 +246,10 @@ function file_entity_install() {
  * Implements hook_uninstall().
  */
 function file_entity_uninstall() {
+  drupal_load('module', 'file_entity');
+  foreach (file_type_get_all_types(TRUE) as $type) {
+    file_type_delete($type);
+  }
   db_drop_field('file_managed', 'type');
 }
 
@@ -317,3 +410,118 @@ function file_entity_update_7200() {
   );
   db_create_table('image_dimensions', $schema['image_dimensions']);
 }
+
+/**
+ * Add the {file_type}, {file_type_mimetypes} and {file_type_streams} tables.
+ */
+function file_entity_update_7201() {
+  $schema = array(
+    'description' => 'Stores the settings for file types.',
+    'fields' => array(
+      'type' => array(
+        'description' => 'The machine name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'label' => array(
+        'description' => 'The human readable name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'translatable' => TRUE,
+      ),
+      'description' => array(
+        'description' => 'A brief description of this file type.',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'medium',
+        'translatable' => TRUE,
+      ),
+    ),
+    'primary key' => array('type'),
+    'export' => array(
+      'key' => 'type',
+      'key name' => 'Type',
+      'primary key' => 'type',
+      'default hook' => 'file_default_types',
+      'identifier' => 'file_type',
+      'export type string' => 'ctools_type',
+      'subrecords callback' => 'file_type_load_subrecords',
+      'save callback' => 'file_type_save',
+      'delete callback' => 'file_type_delete',
+      'api' => array(
+        'owner' => 'file_entity',
+        'api' => 'file_type',
+        'minimum_version' => 1,
+        'current_version' => 1,
+      ),
+    ),
+  ); 
+  db_create_table('file_type', $schema);
+  
+  $schema = array(
+    'description' => 'Maps mimetypes to file types.',
+    'fields' => array(
+      'type' => array(
+        'description' => 'The machine name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'mimetype' => array(
+        'description' => 'Mimetypes mapped to this file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      array('file_type' => 'type'),
+      array('file_type_mimetype' => 'mimetype'),
+    ),
+  );
+  db_create_table('file_type_mimetypes',  $schema);
+  
+  $schema = array(
+    'description' => 'Maps mimetypes to file types.',
+    'fields' => array(
+      'type' => array(
+        'description' => 'The machine name of the file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'stream' => array(
+        'description' => 'Streams mapped to this file type.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'indexes' => array(
+      array('file_type' => 'type'),
+      array('file_type_stream' => 'stream'),
+    ),
+  );
+  db_create_table('file_type_streams',  $schema);
+}
+
+/**
+ * Drupal 7.8 disallows empty string as the value for a bundle key, so update
+ * empty {file_managed}.type records to 'undefined' instead.
+ */
+function file_entity_update_7202() {
+  db_update('file_managed')
+    // Using 'undefined' instead of FILE_TYPE_NONE, because update functions can
+    // run for disabled modules.
+    ->fields(array('type' => 'undefined'))
+    ->condition('type', '')
+    ->execute();
+}
diff --git a/file_entity.module b/file_entity.module
index 20335cd..f054c0d 100644
--- a/file_entity.module
+++ b/file_entity.module
@@ -36,6 +36,8 @@ function file_entity_hook_info() {
     'file_view',
     'file_view_alter',
     'file_displays_alter',
+    'file_type',
+    'file_type_alter',
   );
 
   return array_fill_keys($hooks, array('group' => 'file'));
@@ -101,7 +103,7 @@ function file_entity_menu() {
     'access arguments' => array('administer site configuration'),
     'file' => 'file_entity.admin.inc',
   );
-  $items['admin/structure/file-types/manage/%'] = array(
+  $items['admin/structure/file-types/manage/%file_type'] = array(
     'title' => 'Manage file types',
     'description' => 'Manage settings for the type of files used on your site.',
   );
@@ -193,6 +195,21 @@ function file_entity_menu() {
       // router path).
       $file_type_argument = isset($bundle_info['admin']['bundle argument']) ? $bundle_info['admin']['bundle argument'] : $file_type;
 
+      $items[$path] = array(
+        'title' => 'Edit file type',
+        'title callback' => 'file_type_get_name',
+        'title arguments' => array(4),
+        'page callback' => 'drupal_get_form',
+        'page arguments' => array('file_entity_file_type_form', $file_type_argument),
+        'file' => 'file_entity.admin.inc',
+      ) + $access;
+
+      // Add the 'File type settings' tab.
+      $items["$path/edit"] = array(
+        'title' => 'Edit',
+        'type' => MENU_DEFAULT_LOCAL_TASK,
+      );
+
       // Add the 'Manage file display' tab.
       $items["$path/file-display"] = array(
         'title' => 'Manage file display',
@@ -342,30 +359,31 @@ function file_entity_theme() {
 /**
  * Implements hook_entity_info_alter().
  *
- * Extends the core file entity to be fieldable. Modules can define file types
- * via hook_file_type_info(). For each defined type, create a bundle, so that
- * fields can be configured per file type.
+ * Extends the core file entity to be fieldable. The file type is used as the
+ * bundle key. File types are implemented as CTools exportables, so modules can
+ * define default file types via hook_file_default_types(), and the
+ * administrator can override the default types or add custom ones via
+ * admin/structure/file-types.
  */
 function file_entity_entity_info_alter(&$entity_info) {
   $entity_info['file']['fieldable'] = TRUE;
   $entity_info['file']['entity keys']['bundle'] = 'type';
+  $entity_info['file']['bundle keys']['bundle'] = 'type';
   $entity_info['file']['bundles'] = array();
   $entity_info['file']['uri callback'] = 'file_entity_uri';
   $entity_info['file']['view modes']['full'] = array(
     'label' => t('Full'),
     'custom settings' => FALSE,
   );
-  foreach (file_info_file_types() as $type => $info) {
-    $info += array(
-      // Provide a default administration path for Field UI, but not if 'admin'
-      // has been explicitly set to NULL.
+  foreach (file_type_get_enabled_types() as $type) {
+    $entity_info['file']['bundles'][$type->type] = array(
+      'label' => $type->label,
       'admin' => array(
-        'path' => 'admin/structure/file-types/manage/%',
-        'real path' => 'admin/structure/file-types/manage/' . $type,
+        'path' => 'admin/structure/file-types/manage/%file_type',
+        'real path' => 'admin/structure/file-types/manage/' . $type->type,
         'bundle argument' => 4,
       ),
     );
-    $entity_info['file']['bundles'][$type] = array_intersect_key($info, drupal_map_assoc(array('label', 'admin')));
   }
 
   // Ensure some of the Entity API callbacks are supported.
@@ -620,7 +638,7 @@ function file_entity_file_formatter_file_image_settings($form, &$form_state, $se
 function _file_entity_view_mode_menu_access($file_type, $view_mode, $access_callback) {
   // Deny access if the view mode isn't configured to use custom display
   // settings.
-  $view_mode_settings = field_view_mode_settings('file', $file_type);
+  $view_mode_settings = field_view_mode_settings('file', $file_type->type);
   $visibility = ($view_mode == 'default') || !empty($view_mode_settings[$view_mode]['custom_settings']);
   if (!$visibility) {
     return FALSE;
@@ -864,6 +882,100 @@ function file_entity_get_hidden_stream_wrappers() {
 }
 
 /**
+ * Implements hook_ctools_plugin_api().
+ */
+function file_entity_ctools_plugin_api($owner, $api) {
+  static $api_versions = array(
+    'file_entity' => array(
+      'file_type' => 1,
+    ),
+  );
+  if (isset($api_versions[$owner][$api])) {
+    return array('version' => $api_versions[$owner][$api]);
+  }
+}
+
+/**
+ * Implements hook_file_default_types().
+ */
+function file_entity_file_default_types() {
+  $types = array();
+
+  // Image
+  $types['image'] = (object) array(
+    'api_version' => 1,
+    'type' => 'image',
+    'label' => t('Image'),
+    'description' => t("An <em>Image</em> is a two-dimensional picture that has a similar appearance to some subject, usually a physical object or a person."),
+    'mimetypes' => array(
+      'image/jpeg',
+      'image/gif',
+      'image/png',
+    ),
+    'streams' => array(
+      'public',
+    ),
+  );
+
+  // Video
+  $types['video'] = (object) array(
+    'api_version' => 1,
+    'type' => 'video',
+    'label' => t('Video'),
+    'description' => t('<em>Videos</em> are a sequence of still images representing scenes in motion.'),
+    'mimetypes' => array(
+      'video/quicktime',
+      'video/mp4',
+      'video/x-msvideo',
+      'video/ogg',
+    ),
+    'streams' => array(
+      'public',
+    ),
+  );
+
+  // Audio
+  $types['audio'] = (object) array(
+    'api_version' => 1,
+    'type' => 'audio',
+    'label' => t('Audio'),
+    'description' => t('<em>Audio</em> files are an electrical representation of sound.'),
+    'mimetypes' => array(
+      'audio/mpeg',
+      'audio/x-ms-wma',
+      'audio/x-wav',
+      'audio/ogg',
+    ),
+    'streams' => array(
+      'public',
+    ),
+  );
+
+  // Document
+  $types['document'] = (object) array(
+    'api_version' => 1,
+    'type' => 'document',
+    'label' => t('Document'),
+    'description' => t('A <em>Document</em> is a work of writing intended to store and communicate information.'),
+    'mimetypes' => array(
+      'text/plain',
+      'application/msword',
+      'application/vnd.ms-excel',
+      'application/pdf',
+      'application/vnd.ms-powerpoint',
+      'application/vnd.oasis.opendocument.text',
+      'application/vnd.oasis.opendocument.spreadsheet',
+      'application/vnd.oasis.opendocument.presentation',
+    ),
+    'streams' => array(
+      'public',
+    ),
+  );
+
+  return $types;
+}
+
+/**
  * Clear the field cache for any entities referencing a specific file.
  *
  * @param object $file
diff --git a/file_entity.tokens.inc b/file_entity.tokens.inc
index 629ea23..dbdd7fa 100644
--- a/file_entity.tokens.inc
+++ b/file_entity.tokens.inc
@@ -73,16 +73,15 @@ function file_entity_tokens($type, $tokens, array $data = array(), array $option
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'type':
-          if ($file_type = file_info_file_types($file->type)) {
-            $replacements[$original] = $sanitize ? check_plain($file_type['label']) : $file_type['label'];
+          if ($file_type = file_type_load($file->type)) {
+            $replacements[$original] = $sanitize ? check_plain($file_type->label) : $file_type->label;
           }
           break;
       }
     }
 
     // Chained token relationships.
-    if (($file_type_tokens = token_find_with_prefix($tokens, 'type')) && $file_type = file_info_file_types($file->type)) {
-      $file_type['type'] = $file->type;
+    if (($file_type_tokens = token_find_with_prefix($tokens, 'type')) && $file_type = file_type_load($file->type)) {
       $replacements += token_generate('file-type', $file_type_tokens, array('file_type' => $file_type), $options);
     }
   }
@@ -94,21 +93,21 @@ function file_entity_tokens($type, $tokens, array $data = array(), array $option
     foreach ($tokens as $name => $original) {
       switch ($name) {
         case 'name':
-          $replacements[$original] = $sanitize ? check_plain($file_type['label']) : $file_type['label'];
+          $replacements[$original] = $sanitize ? check_plain($file_type->label) : $file_type->label;
           break;
         case 'machine-name':
           // This is a machine name so does not ever need to be sanitized.
-          $replacements[$original] = $file_type['type'];
+          $replacements[$original] = $file_type->type;
           break;
         case 'count':
           $query = db_select('file_managed');
-          $query->condition('type', $file_type['type']);
+          $query->condition('type', $file_type->type);
           $query->addTag('file_type_file_count');
           $count = $query->countQuery()->execute()->fetchField();
           $replacements[$original] = (int) $count;
           break;
         case 'edit-url':
-          $replacements[$original] = url('admin/structure/file-types/manage/' . $file_type['type'] . '/fields', $url_options);
+          $replacements[$original] = url('admin/structure/file-types/manage/' . $file_type->type . '/fields', $url_options);
           break;
       }
     }
diff --git a/tests/file_entity.test b/tests/file_entity.test
index 4355fc6..2799ef4 100644
--- a/tests/file_entity.test
+++ b/tests/file_entity.test
@@ -23,6 +23,22 @@ class FileEntityTestHelper extends DrupalWebTestCase {
       }
     }
   }
+
+  protected function createFileType($overrides = array()) {
+    $type = new stdClass();
+    $type->type = 'test';
+    $type->label = "Test";
+    $type->description = '';
+    $type->mimetypes = array('image/jpeg', 'image/gif', 'image/png', 'image/tiff');
+    $type->streams = array('public', 'private');
+
+    foreach ($overrides as $k => $v) {
+      $type->$k = $v;
+    }
+
+    file_type_save($type);
+    return $type;
+  }
 }
 
 class FileEntityUnitTestCase extends FileEntityTestHelper {
@@ -55,7 +71,7 @@ class FileEntityUnitTestCase extends FileEntityTestHelper {
 
     // Test entity ID, revision ID, and bundle.
     $ids = entity_extract_ids('file', $file);
-    $this->assertIdentical($ids, array($file->fid, NULL, 'text'));
+    $this->assertIdentical($ids, array($file->fid, NULL, 'document'));
 
     // Test the entity URI callback.
     $uri = entity_uri('file', $file);
@@ -136,7 +152,7 @@ class FileEntityReplaceTestCase extends FileEntityTestHelper {
 
     // Test that file saves without uploading a file.
     $this->drupalPost(NULL, array(), t('Save'));
-    $this->assertText(t('Text @file has been updated.', array('@file' => $file->filename)), 'File was updated without file upload.');
+    $this->assertText(t('Document @file has been updated.', array('@file' => $file->filename)), 'File was updated without file upload.');
 
     // Get the next text file to use as a replacement.
     $original = clone $file;
@@ -146,7 +162,7 @@ class FileEntityReplaceTestCase extends FileEntityTestHelper {
     $edit = array();
     $edit['files[replace_upload]'] = drupal_realpath($replacement->uri);
     $this->drupalPost('file/' . $file->fid . '/edit', $edit, t('Save'));
-    $this->assertText(t('Text @file has been updated.', array('@file' => $file->filename)), 'File was updated with file upload.');
+    $this->assertText(t('Document @file has been updated.', array('@file' => $file->filename)), 'File was updated with file upload.');
 
     // Re-load the file from the database.
     $file = file_load($file->fid);
@@ -196,9 +212,9 @@ class FileEntityTokenTestCase extends FileEntityTestHelper {
 
   function testFileEntityTokens() {
     $tokens = array(
-      'type' => 'Text',
-      'type:name' => 'Text',
-      'type:machine-name' => 'text',
+      'type' => 'Document',
+      'type:name' => 'Document',
+      'type:machine-name' => 'document',
       'type:count' => count($this->files['text']),
     );
     $this->assertTokens('file', array('file' => $this->files['text'][0]), $tokens);
@@ -233,3 +249,47 @@ class FileEntityTokenTestCase extends FileEntityTestHelper {
     return $values;
   }
 }
+
+class FileEntityTypeTestCase extends FileEntityTestHelper {
+  public static function getInfo() {
+    return array(
+      'name' => 'File entity types',
+      'description' => 'Test the file entity types.',
+      'group' => 'File entity',
+    );
+  }
+
+  /**
+   * Test creating a new type. Basic CRUD.
+   */
+  function testCreate() {
+    $type_machine_type = 'foo';
+    $type_machine_label = 'foobar';
+    $type = $this->createFileType(array('type' => $type_machine_type, 'label' => $type_machine_label));
+    $loaded_type = file_type_load($type_machine_type);
+    $this->assertEqual($loaded_type->label, $type_machine_label, "Was able to create a type and retreive it.");
+  }
+
+
+  /**
+   * Ensures that the weight is respected when types are created.
+   * @return unknown_type
+   */
+  function testOrder() {
+//    $type = $this->createFileType(array('name' => 'last', 'label' => 'Last', 'weight' => 100));
+//    $type = $this->createFileType(array('name' => 'first', 'label' => 'First'));
+//    $types = media_type_get_types();
+//    $keys = array_keys($types);
+//    $this->assertTrue(isset($types['last']) && isset($types['first']), "Both types saved");
+//    $this->assertTrue(array_search('last', $keys) > array_search('first', $keys), 'Type which was supposed to be first came first');
+  }
+
+  /**
+   * Test view mode assignment.  Currently fails, don't know why.
+   * @return unknown_type
+   */
+  function testViewModesAssigned() {
+  }
+
+
+}
diff --git a/tests/file_entity_test.module b/tests/file_entity_test.module
index 7879703..c6b1dfd 100644
--- a/tests/file_entity_test.module
+++ b/tests/file_entity_test.module
@@ -5,96 +5,11 @@
  * File Entity Test
  */
 
-
 /**
  * Implements hook_menu().
  */
 function file_entity_test_menu() {
   $items = array();
 
-  $items['file-entity-test/file/add'] = array(
-    'title' => 'Add file',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('file_entity_test_add_form'),
-    'access arguments' => array('administer site configuration'),
-    'file' => 'file_entity_test.pages.inc',
-  );
-  $items['file-entity-test/file/%file'] = array(
-    'title' => 'View file',
-    'page callback' => 'file_entity_test_view_page',
-    'page arguments' => array(2),
-    'access arguments' => array('administer site configuration'),
-    'file' => 'file_entity_test.pages.inc',
-  );
-  $items['file-entity-test/file/%file/view'] = array(
-    'title' => 'View',
-    'type' => MENU_DEFAULT_LOCAL_TASK,
-    'weight' => -10,
-  );
-  $items['file-entity-test/file/%file/preview'] = array(
-    'title' => 'Preview',
-    'page callback' => 'file_entity_test_preview_page',
-    'page arguments' => array(2),
-    'access arguments' => array('administer site configuration'),
-    'weight' => 0,
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'file_entity_test.pages.inc',
-  );
-  $items['file-entity-test/file/%file/edit'] = array(
-    'title' => 'Edit',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('file_entity_test_edit_form', 2),
-    'access arguments' => array('administer site configuration'),
-    'weight' => 1,
-    'type' => MENU_LOCAL_TASK,
-    'file' => 'file_entity_test.pages.inc',
-  );
-
   return $items;
 }
-
-/**
- * Implements hook_file_type_info().
- */
-function file_entity_test_file_type_info() {
-  return array(
-    'file_entity_test' => array(
-      'label' => t('Test'),
-      'description' => t('A file type defined by the File Entity Test module. Used for testing only.'),
-      'claim callback' => 'file_entity_test_file_type_file_entity_test_claim',
-      'default view callback' => 'file_entity_test_file_type_file_entity_test_default_view',
-      'weight' => 100,
-    ),
-  );
-}
-
-/**
- * Implements hook_file_type_TYPE_claim().
- *
- * Returns TRUE if the passed in file should be assigned the 'file_entity_test'
- * file type.
- */
-function file_entity_test_file_type_file_entity_test_claim($file) {
-  return TRUE;
-}
-
-/**
- * Implements hook_file_type_TYPE_default_view().
- */
-function file_entity_test_file_type_file_entity_test_default_view($file, $view_mode, $langcode) {
-  return array(
-    '#type' => 'link',
-    '#title' => $file->filename,
-    '#href' => file_create_url($file->uri),
-  );
-}
-
-/**
- * Implements hook_entity_info_alter().
- */
-function file_entity_test_entity_info_alter(&$entity_info) {
-  $entity_info['file']['view modes']['file_entity_test_preview'] = array(
-    'label' => t('Test Preview'),
-    'custom settings' => TRUE,
-  );
-}
diff --git a/tests/file_entity_test.pages.inc b/tests/file_entity_test.pages.inc
index 9bea905..4b608f7 100644
--- a/tests/file_entity_test.pages.inc
+++ b/tests/file_entity_test.pages.inc
@@ -4,90 +4,3 @@
  * @file
  * Test pages for the File Entity Test module.
  */
-
-/**
- * Form callback; upload a file.
- */
-function file_entity_test_add_form($form, &$form_state) {
-  $form['file'] = array(
-    '#type' => 'managed_file',
-    '#required' => TRUE,
-    '#title' => 'File',
-    '#upload_location' => 'public://',
-  );
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Form submit callback; save the uploaded file.
- */
-function file_entity_test_add_form_submit($form, &$form_state) {
-  $file = file_load($form_state['values']['file']);
-  if (!$file->status) {
-    $file->status = FILE_STATUS_PERMANENT;
-    file_save($file);
-  }
-  drupal_set_message(t('Your file has been saved.'));
-  $form_state['redirect'] = 'file-entity-test/file/' . $file->fid;
-}
-
-/**
- * Page callback; view a file.
- */
-function file_entity_test_view_page($file) {
-  return file_view($file, 'full');
-}
-
-/**
- * Page callback; preview a file.
- */
-function file_entity_test_preview_page($file) {
-  return file_view($file, 'file_entity_test_preview');
-}
-
-/**
- * Form callback; edit a file.
- */
-function file_entity_test_edit_form($form, &$form_state, $file) {
-  $form_state['file'] = $file;
-  field_attach_form('file', $file, $form, $form_state);
-  $form['file'] = file_view($file, 'file_entity_test_preview');
-
-  // Add internal file properties needed by
-  // file_entity_test_edit_form_validate().
-  foreach (array('fid', 'type') as $key) {
-    $form[$key] = array('#type' => 'value', '#value' => $file->$key);
-  }
-
-  $form['actions'] = array('#type' => 'actions');
-  $form['actions']['submit'] = array(
-    '#type' => 'submit',
-    '#value' => t('Save'),
-  );
-
-  return $form;
-}
-
-/**
- * Form validation handler for the file edit form.
- */
-function file_entity_test_edit_form_validate($form, &$form_state) {
-  entity_form_field_validate('file', $form, $form_state);
-}
-
-/**
- * Form submit handler for the file edit form
- */
-function file_entity_test_edit_form_submit($form, &$form_state) {
-  $file = $form_state['file'];
-  entity_form_submit_build_entity('file', $file, $form, $form_state);
-  file_save($file);
-  drupal_set_message(t('Your changes to the file have been saved.'));
-  $form_state['redirect'] = 'file-entity-test/file/' . $file->fid;
-}
