Entities in Drupal 7 are really powerful. Predominantly because of the Fields module that can dynamically implement properties onto an Entity bundle through the Fields UI. This makes the Fields and Fields UI modules the key modules for driver content view and edit pages.

You're probably already familar with the "Manage Fields" and "Manage Display" options provided under Structure > Content Types under the Drupal Admin part of a site. Manage Fields lets you define Fields and order them by how they should be presented in an edit or create form for that content type. While Manage Display lets you order the fields per view mode, like Teaser or Full page view.

You may have also noticed some non-editable fields appear in these interfaces such as the node title or URL redirects or the poll fields from the Poll module. These are known as Extra Fields and are typically used to expose entity properties that are not managed by Fields through the Fields UI interface. This is useful for modules that manage their own data attached to an entity or if you want to expose another modules property via Fields UI.

Functions to implement Extra Fields

Implementing Extra Fields is somewhat unintuitive. While most APIs in Drupal use a common hook system, e.g. hook_node_view, hook_node_update, hook_node_delete; the Extra Fields API only provides one hook and utilizes a couple pre-existing hooks. So here is a quick tutorial on how to fully implement Extra Fields. In this tutorial, we're going to expose the node module's created and updated date on the article content type to the Fields UI module. An example module can be found on GitHub

1. Tell Fields UI about your extra fields with hook_field_extra_fields

This is the only hook Fields UI provides and it tells it what extra fields your module provides. In our example, we're providing the created and updated date of the article content type.

/**
 * Implements hook_field_extra_fields().
 */
function example_field_extra_fields() {
  $extra['node']['article'] = array(
    'display' => array(
      'updated' => array(
        'label' => t('Last updated date'),
        'description' => t('Display the date the article was last updated.'),
        'weight' => 99,
      ),
      'created' => array(
        'label' => t('Created date'),
        'description' => t('Display the date the article was created.'),
        'weight' => 100,
        'visible' => FALSE,
      ),
    ),
  );
  return $extra;
}

In the above example we told the Fields UI module that we provide two display fields. These are fields that are rendered with Drupal's view modes. We can also choose to display form fields. These are fields that are rendered when the form is shown.

2. Tell the node module to render your fields with hook_node_view

Next we want to see the fields actually display. In this case we want the fields to be renderable in the view modes because they are display fields opposed to edit fields. Otherwise, we'd implement hook_form_alter

/**
 * Implements hook_node_view().
 */
function example_node_view($node, $view_mode, $langcode) {
  $extra = example_field_extra_fields();

  // Check that we're supporting the node type being viewed.
  if (empty($extra['node'][$node->type]['display'])) {
    return;
  }

  $config = field_bundle_settings('node', $node->type);
  foreach ($extra['node'][$node->type]['display'] as $field_name => $field_info) {
    // Check to make sure this field is visible in this view mode.
    if (empty($config['extra_fields']['display'][$field_name][$view_mode]['visible'])) {
      continue;
    }

    $timestamp  = $node->{$field_name};
    $element = array(
      '#tag' => 'span',
      '#value' => format_date($timestamp),
    );
    $attributes = array();
    if (module_exists('rdf')) {
      $attributes = rdf_rdfa_attributes($node->rdf_mapping[$date_field], $timestamp);
    }
    $attributes['class'] = $field_name;
    $attributes['datetime'] = date_iso8601($timestamp);
    $element['#attributes'] = $attributes;
    $variables['element'] = $element;

    $node->content[$field_name]['#markup'] = theme('html_tag', $variables);
  }
}

No need to worry about weight ordering of the field. Fields UI will take care of that for you. All you need to worry about is not rendering the field if its not being displayed which is why we check $config['extra_fields']['display'][$field_name][$view_mode]['visible']. This is doubly important if rendering your field can be expensive. You don't want to waste CPU time on things you don't want to see.

Exposing configuration options to manage your field.

Now that you have exposed your field via the Manage fields interface, if you want to provide the user with additional visual configuration options for that field, you'll have to use good ol hook_form_alter() to alter the field and add them manually.

/**
 * Implements hook_form_field_ui_display_overview_form_alter().
 */
function example_form_field_ui_display_overview_form_alter(&$form, &$form_state) {
  // Load the extra fields from hook_field_extra_fields() for the example module.
  $extra = example_field_extra_fields();

  // If the fields are not supported for the entity type and bundle we're loading the form for
  // then return early.
  if (!isset($extra[$form['#entity_type']], $extra[$form['#entity_type']][$form['#bundle']])) {
    return;
  }

  // Filter extra fields by those the field ui module is chosing to show in the form.
  $extra_fields = array_keys($extra[$form['#entity_type']][$form['#bundle']]['display']);
  $extra_fields = array_intersect($extra_fields, $form['#extra']);

  if (empty($extra_fields)) {
    return;
  }

  foreach ($extra_fields as $field_name) {
    $form['fields'][$field_name]['settings_edit'] = array(
      '#type' => 'textfield',
      '#default_value' => t('example text field as a configuration option.'),
    );
    $form['fields'][$field_name]['settings_summary']['#markup'] = 'This is the settings summary.';
  }
  // Don't forget to add a submit handler if you need to process/save 
  // configuration for your fields.
  // $form['#submit'][] = 'example_form_field_ui_display_overview_form_submit';
}