diff --git a/MultifieldViewsHandler.php b/MultifieldViewsHandler.php
index 0000000..7a09f9d
--- /dev/null
+++ b/MultifieldViewsHandler.php
@@ -0,0 +1,916 @@
+<?php
+
+class MultifieldViewsHandler extends views_handler_field {
+
+  /**
+   * An array to store field renderable arrays for use by render_items.
+   *
+   * @var array
+   */
+  public $items = array();
+
+  /**
+   * Store the field information.
+   *
+   * @var array
+   */
+  public $multifield_info = array();
+
+  /**
+   * Store the subfield information.
+   *
+   * @var array
+   */
+  public $field_info = array();
+
+  /**
+   * Does the field supports multiple field values.
+   *
+   * @var bool
+   */
+  public $multiple;
+
+  /**
+   * Does the rendered fields get limited.
+   *
+   * @var bool
+   */
+  public $limit_values;
+
+  /**
+   * A shortcut for $view->base_table.
+   *
+   * @var string
+   */
+  public $base_table;
+
+  /**
+   * Store the field instance.
+   *
+   * @var array
+   */
+  public $instance;
+
+  public $fieldHandler;
+
+  private $init_options;
+
+  public function construct() {
+    parent::construct();
+    $definition = $this->definition;
+    $definition['handler'] = $definition['subfield handler'];
+    unset($definition['subfield handler']);
+    $definition['field_name'] = $definition['subfield_name'];
+    unset($definition['subfield_name']);
+    $this->fieldHandler = _views_create_handler($definition, 'handler', 'field');
+    if ($this->view && $this->init_options) {
+      $this->fieldHandler->init($this->view, $this->init_options);
+    }
+  }
+
+  function init(&$view, &$options) {
+    $this->init_options = $options;
+    parent::init($view, $options);
+    $this->multifield_info = $field = field_info_field($this->definition['field_name']);
+    $this->field_info = $subfield = field_info_field($this->definition['subfield_name']);
+    $this->multiple = FALSE;
+    $this->limit_values = FALSE;
+
+    if ($field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
+      $this->multiple = TRUE;
+
+      // If "Display all values in the same row" is FALSE, then we always limit
+      // in order to show a single unique value per row.
+      if (!$this->options['group_rows']) {
+        $this->limit_values = TRUE;
+      }
+
+      // If "First and last only" is chosen, limit the values
+      if (!empty($this->options['delta_first_last'])) {
+        $this->limit_values = TRUE;
+      }
+
+      // Otherwise, we only limit values if the user hasn't selected "all", 0, or
+      // the value matching field cardinality.
+      if ((intval($this->options['delta_limit']) && ($this->options['delta_limit'] != $field['cardinality'])) || intval($this->options['delta_offset'])) {
+        $this->limit_values = TRUE;
+      }
+    }
+
+    // Convert old style entity id group column to new format.
+    // @todo Remove for next major version.
+    if ($this->options['group_column'] == 'entity id') {
+      $this->options['group_column'] = 'entity_id';
+    }
+    if ($this->fieldHandler) {
+      $this->fieldHandler->init($view, $options);
+    }
+  }
+
+  /**
+   * Check whether current user has access to this handler.
+   *
+   * @return bool
+   *   Return TRUE if the user has access to view this field.
+   */
+  /*function access() {
+    $base_table = $this->get_base_table();
+    return field_access('view', $this->field_info, $this->definition['entity_tables'][$base_table]);
+  }*/
+
+  /**
+   * Set the base_table and base_table_alias.
+   *
+   * @return string
+   *   The base table which is used in the current view "context".
+   */
+  function get_base_table() {
+    if (!isset($this->base_table)) {
+      // This base_table is coming from the entity not the field.
+      $this->base_table = $this->view->base_table;
+
+      // If the current field is under a relationship you can't be sure that the
+      // base table of the view is the base table of the current field.
+      // For example a field from a node author on a node view does have users as base table.
+      if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
+        $relationships = $this->view->display_handler->get_option('relationships');
+        if (!empty($relationships[$this->options['relationship']])) {
+          $options = $relationships[$this->options['relationship']];
+          $data = views_fetch_data($options['table']);
+          $this->base_table = $data[$options['field']]['relationship']['base'];
+        }
+      }
+    }
+
+    return $this->base_table;
+  }
+
+  /**
+   * Called to add the field to a query.
+   *
+   * By default, the only columns added to the query are entity_id and
+   * entity_type. This is because other needed data is fetched by entity_load().
+   * Other columns are added only if they are used in groupings, or if
+   * 'add fields to query' is specifically set to TRUE in the field definition.
+   *
+   * The 'add fields to query' switch is used by modules which need all data
+   * present in the query itself (such as "sphinx").
+   */
+  function query($use_groupby = FALSE) {
+    $this->get_base_table();
+
+    $params = array();
+    if ($use_groupby) {
+      // When grouping on a "field API" field (whose "real_field" is set to
+      // entity_id), retrieve the minimum entity_id to have a valid entity_id to
+      // pass to field_view_field().
+      $params = array(
+        'function' => 'min',
+      );
+
+      $this->ensure_my_table();
+    }
+
+    // Get the entity type according to the base table of the field.
+    // Then add it to the query as a formula. That way we can avoid joining
+    // the field table if all we need is entity_id and entity_type.
+    $entity_type = $this->definition['entity_tables'][$this->base_table];
+    $entity_info = entity_get_info($entity_type);
+
+    if (isset($this->relationship)) {
+      $this->base_table_alias = $this->relationship;
+    }
+    else {
+      $this->base_table_alias = $this->base_table;
+    }
+
+    // We always need the base field (entity_id / revision_id).
+    if (empty($this->definition['is revision'])) {
+      $this->field_alias = $this->query->add_field($this->base_table_alias, $entity_info['entity keys']['id'], '', $params);
+    }
+    else {
+      $this->field_alias = $this->query->add_field($this->base_table_alias, $entity_info['entity keys']['revision'], '', $params);
+      $this->aliases['entity_id'] = $this->query->add_field($this->base_table_alias, $entity_info['entity keys']['id'], '', $params);
+    }
+
+
+    // The alias needs to be unique, so we use both the field table and the entity type.
+    $entity_type_alias = $this->definition['table'] . '_' . $entity_type . '_entity_type';
+    $this->aliases['entity_type'] = $this->query->add_field(NULL, "'$entity_type'", $entity_type_alias);
+
+    $fields = $this->additional_fields;
+    // We've already added entity_type, so we can remove it from the list.
+    $entity_type_key = array_search('entity_type', $fields);
+    if ($entity_type_key !== FALSE) {
+      unset($fields[$entity_type_key]);
+    }
+
+    if ($use_groupby) {
+      // Add the fields that we're actually grouping on.
+      $options = array();
+
+      if ($this->options['group_column'] != 'entity_id') {
+        $options = array($this->options['group_column'] => $this->options['group_column']);
+      }
+
+      $options += is_array($this->options['group_columns']) ? $this->options['group_columns'] : array();
+
+
+      $fields = array();
+      $rkey = $this->definition['is revision'] ? 'FIELD_LOAD_REVISION' : 'FIELD_LOAD_CURRENT';
+      // Go through the list and determine the actual column name from field api.
+      foreach ($options as $column) {
+        $name = $column;
+        if (isset($this->multifield_info['storage']['details']['sql'][$rkey][$this->table][$column])) {
+          $name = $this->multifield_info['storage']['details']['sql'][$rkey][$this->table][$column];
+        }
+
+        $fields[$column] = $name;
+      }
+
+      $this->group_fields = $fields;
+    }
+
+    // Add additional fields (and the table join itself) if needed.
+    if ($this->add_field_table($use_groupby)) {
+      $this->ensure_my_table();
+      $this->add_additional_fields($fields);
+
+      // Filter by language, if field translation is enabled.
+      $field = $this->multifield_info;
+      if (field_is_translatable($entity_type, $field) && !empty($this->view->display_handler->options['field_language_add_to_query'])) {
+        $column = $this->table_alias . '.language';
+        // By the same reason as field_language the field might be LANGUAGE_NONE in reality so allow it as well.
+        // @see this::field_language()
+        global $language_content;
+        $default_language = language_default('language');
+        $language = str_replace(array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'), array($language_content->language, $default_language), $this->view->display_handler->options['field_language']);
+        $placeholder = $this->placeholder();
+        $language_fallback_candidates = array($language);
+        if (variable_get('locale_field_language_fallback', TRUE)) {
+          require_once DRUPAL_ROOT . '/includes/language.inc';
+          $language_fallback_candidates = array_merge($language_fallback_candidates, language_fallback_get_candidates());
+        }
+        else {
+          $language_fallback_candidates[] = LANGUAGE_NONE;
+        }
+        $this->query->add_where_expression(0, "$column IN($placeholder) OR $column IS NULL", array($placeholder => $language_fallback_candidates));
+      }
+    }
+
+    // The revision id inhibits grouping.
+    // So, stop here if we're using grouping, or if aren't adding all columns to
+    // the query.
+    if ($use_groupby || empty($this->definition['add fields to query'])) {
+      return;
+    }
+
+    $this->add_additional_fields(array('revision_id'));
+  }
+
+  /**
+   * Determine if the field table should be added to the query.
+   */
+  function add_field_table($use_groupby) {
+    // Grouping is enabled, or we are explicitly required to do this.
+    if ($use_groupby || !empty($this->definition['add fields to query'])) {
+      return TRUE;
+    }
+    // This a multiple value field, but "group multiple values" is not checked.
+    if ($this->multiple && !$this->options['group_rows']) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Determine if this field is click sortable.
+   */
+  function click_sortable() {
+    // Not click sortable in any case.
+    if (empty($this->definition['click sortable'])) {
+      return FALSE;
+    }
+    // A field is not click sortable if it's a multiple field with
+    // "group multiple values" checked, since a click sort in that case would
+    // add a join to the field table, which would produce unwanted duplicates.
+    if ($this->multiple && $this->options['group_rows']) {
+      return FALSE;
+    }
+    return TRUE;
+  }
+
+  /**
+   * Called to determine what to tell the clicksorter.
+   */
+  function click_sort($order) {
+    // No column selected, can't continue.
+    if (empty($this->options['click_sort_column'])) {
+      return;
+    }
+
+    $this->ensure_my_table();
+    $column = _field_sql_storage_columnname($this->definition['field_name'], _field_sql_storage_columnname($this->definition['subfield_name'], $this->options['click_sort_column']));
+    if (!isset($this->aliases[$column])) {
+      // Column is not in query; add a sort on it (without adding the column).
+      $this->aliases[$column] = $this->table_alias . '.' . $column;
+    }
+    $this->query->add_orderby(NULL, NULL, $order, $this->aliases[$column]);
+  }
+
+  function option_definition() {
+    $options = parent::option_definition();
+
+    // option_definition runs before init/construct, so no $this->field_info
+    $field = field_info_field($this->definition['subfield_name']);
+    $field_type = field_info_field_types($field['type']);
+    $column_names = array_keys($field['columns']);
+    $default_column = '';
+    // Try to determine a sensible default.
+    if (count($column_names) == 1) {
+      $default_column = $column_names[0];
+    }
+    elseif (in_array('value', $column_names)) {
+      $default_column = 'value';
+    }
+
+    // If the field has a "value" column, we probably need that one.
+    $options['click_sort_column'] = array(
+      'default' => $default_column,
+    );
+    $options['type'] = array(
+      'default' => $field_type['default_formatter'],
+    );
+    $options['settings'] = array(
+      'default' => array(),
+    );
+    $options['group_column'] = array(
+      'default' => $default_column,
+    );
+    $options['group_columns'] = array(
+      'default' => array(),
+    );
+
+    // Options used for multiple value fields.
+    $options['group_rows'] = array(
+      'default' => TRUE,
+      'bool' => TRUE,
+    );
+    // If we know the exact number of allowed values, then that can be
+    // the default. Otherwise, default to 'all'.
+    $options['delta_limit'] = array(
+      'default' => ($field['cardinality'] > 1) ? $field['cardinality'] : 'all',
+    );
+    $options['delta_offset'] = array(
+      'default' => 0,
+    );
+    $options['delta_reversed'] = array(
+      'default' => FALSE,
+      'bool' => TRUE,
+    );
+    $options['delta_first_last'] = array(
+      'default' => FALSE,
+      'bool' => TRUE,
+    );
+
+    $options['multi_type'] = array(
+      'default' => 'separator',
+    );
+    $options['separator'] = array(
+      'default' => ', ',
+    );
+
+    $options['field_api_classes'] = array(
+      'default' => FALSE,
+      'bool' => TRUE,
+    );
+
+    return $options;
+  }
+
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    module_load_include('inc', 'views', 'modules/field/views_handler_field_field');
+    $subfield = $this->field_info;
+    $formatters = _field_view_formatter_options($subfield['type']);
+    $column_names = array_keys($subfield['columns']);
+
+    // If this is a multiple value field, add its options.
+    if ($this->multiple) {
+      $this->multiple_options_form($form, $form_state);
+    }
+
+    // No need to ask the user anything if the field has only one column.
+    if (count($subfield['columns']) == 1) {
+      $form['click_sort_column'] = array(
+        '#type' => 'value',
+        '#value' => isset($column_names[0]) ? $column_names[0] : '',
+      );
+    }
+    else {
+      $form['click_sort_column'] = array(
+        '#type' => 'select',
+        '#title' => t('Column used for click sorting'),
+        '#options' => drupal_map_assoc($column_names),
+        '#default_value' => $this->options['click_sort_column'],
+        '#description' => t('Used by Style: Table to determine the actual column to click sort the field on. The default is usually fine.'),
+        '#fieldset' => 'more',
+      );
+    }
+
+    $form['type'] = array(
+      '#type' => 'select',
+      '#title' => t('Formatter'),
+      '#options' => $formatters,
+      '#default_value' => $this->options['type'],
+      '#ajax' => array(
+        'path' => views_ui_build_form_url($form_state),
+      ),
+      '#submit' => array('views_ui_config_item_form_submit_temporary'),
+      '#executes_submit_callback' => TRUE,
+    );
+
+    $form['field_api_classes'] = array(
+      '#title' => t('Use field template'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['field_api_classes'],
+      '#description' => t('If checked, field api classes will be added using field.tpl.php (or equivalent). This is not recommended unless your CSS depends upon these classes. If not checked, template will not be used.'),
+      '#fieldset' => 'style_settings',
+      '#weight' => 20,
+    );
+
+    if ($this->multiple) {
+      $form['field_api_classes']['#description'] .= ' ' . t('Checking this option will cause the group Display Type and Separator values to be ignored.');
+    }
+
+    // Get the currently selected formatter.
+    $format = $this->options['type'];
+
+    $formatter = field_info_formatter_types($format);
+    $settings = $this->options['settings'] + field_info_formatter_settings($format);
+
+    // Provide an instance array for hook_field_formatter_settings_form().
+    ctools_include('fields');
+    $this->instance = ctools_fields_fake_field_instance($this->definition['subfield_name'], '_custom', $formatter, $settings);
+
+    // Store the settings in a '_custom' view mode.
+    $this->instance['display']['_custom'] = array(
+      'type' => $format,
+      'settings' => $settings,
+    );
+
+    // Get the settings form.
+    $settings_form = array('#value' => array());
+    $function = $formatter['module'] . '_field_formatter_settings_form';
+    if (function_exists($function)) {
+      $settings_form = $function($subfield, $this->instance, '_custom', $form, $form_state);
+    }
+    $form['settings'] = $settings_form;
+  }
+
+  /**
+   * Provide options for multiple value fields.
+   */
+  function multiple_options_form(&$form, &$form_state) {
+    $field = $this->multifield_info;
+
+    $form['multiple_field_settings'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Multiple field settings'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#weight' => 5,
+    );
+
+    $form['group_rows'] = array(
+      '#title' => t('Display all values in the same row'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['group_rows'],
+      '#description' => t('If checked, multiple values for this field will be shown in the same row. If not checked, each value in this field will create a new row. If using group by, please make sure to group by "Entity ID" for this setting to have any effect.'),
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    // Make the string translatable by keeping it as a whole rather than
+    // translating prefix and suffix separately.
+    list($prefix, $suffix) = explode('@count', t('Display @count value(s)'));
+
+    if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
+      $type = 'textfield';
+      $options = NULL;
+      $size = 5;
+    }
+    else {
+      $type = 'select';
+      $options = drupal_map_assoc(range(1, $field['cardinality']));
+      $size = 1;
+    }
+    $form['multi_type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#dependency' => array('edit-options-group-rows' => array(TRUE)),
+      '#default_value' => $this->options['multi_type'],
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#dependency' => array(
+        'radio:options[multi_type]' => array('separator'),
+        'edit-options-group-rows' => array(TRUE),
+      ),
+      '#dependency_count' => 2,
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    $form['delta_limit'] = array(
+      '#type' => $type,
+      '#size' => $size,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#options' => $options,
+      '#default_value' => $this->options['delta_limit'],
+      '#prefix' => '<div class="container-inline">',
+      '#dependency' => array('edit-options-group-rows' => array(TRUE)),
+      '#fieldset' => 'multiple_field_settings',
+    );
+
+    list($prefix, $suffix) = explode('@count', t('starting from @count'));
+    $form['delta_offset'] = array(
+      '#type' => 'textfield',
+      '#size' => 5,
+      '#field_prefix' => $prefix,
+      '#field_suffix' => $suffix,
+      '#default_value' => $this->options['delta_offset'],
+      '#dependency' => array('edit-options-group-rows' => array(TRUE)),
+      '#description' => t('(first item is 0)'),
+      '#fieldset' => 'multiple_field_settings',
+    );
+    $form['delta_reversed'] = array(
+      '#title' => t('Reversed'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['delta_reversed'],
+      '#suffix' => $suffix,
+      '#dependency' => array('edit-options-group-rows' => array(TRUE)),
+      '#description' => t('(start from last values)'),
+      '#fieldset' => 'multiple_field_settings',
+    );
+    $form['delta_first_last'] = array(
+      '#title' => t('First and last only'),
+      '#type' => 'checkbox',
+      '#default_value' => $this->options['delta_first_last'],
+      '#suffix' => '</div>',
+      '#dependency' => array('edit-options-group-rows' => array(TRUE)),
+      '#fieldset' => 'multiple_field_settings',
+    );
+  }
+
+  /**
+   * Extend the groupby form with group columns.
+   */
+  function groupby_form(&$form, &$form_state) {
+    parent::groupby_form($form, $form_state);
+    // With "field API" fields, the column target of the grouping function
+    // and any additional grouping columns must be specified.
+    $group_columns = array(
+      'entity_id' => t('Entity ID'),
+    ) + drupal_map_assoc(array_keys($this->multifield_info['columns']), 'ucfirst');
+
+    $form['group_column'] = array(
+      '#type' => 'select',
+      '#title' => t('Group column'),
+      '#default_value' => $this->options['group_column'],
+      '#description' => t('Select the column of this field to apply the grouping function selected above.'),
+      '#options' => $group_columns,
+    );
+
+    $options = drupal_map_assoc(array('bundle', 'language', 'entity_type'), 'ucfirst');
+
+    // Add on defined fields, noting that they're prefixed with the field name.
+    $form['group_columns'] = array(
+      '#type' => 'checkboxes',
+      '#title' => t('Group columns (additional)'),
+      '#default_value' => $this->options['group_columns'],
+      '#description' => t('Select any additional columns of this field to include in the query and to group on.'),
+      '#options' => $options + $group_columns,
+    );
+  }
+
+  function groupby_form_submit(&$form, &$form_state) {
+    parent::groupby_form_submit($form, $form_state);
+    $item = &$form_state['handler']->options;
+
+    // Add settings for "field API" fields.
+    $item['group_column'] = $form_state['values']['options']['group_column'];
+    $item['group_columns'] = array_filter($form_state['values']['options']['group_columns']);
+  }
+
+  /**
+   * Load the entities for all fields that are about to be displayed.
+   */
+  function post_execute(&$values) {
+    if (!empty($values)) {
+      // Divide the entity ids by entity type, so they can be loaded in bulk.
+      $entities_by_type = array();
+      $revisions_by_type = array();
+      foreach ($values as $key => $object) {
+        if (isset($this->aliases['entity_type']) && isset($object->{$this->aliases['entity_type']}) && isset($object->{$this->field_alias}) && !isset($values[$key]->_field_data[$this->field_alias])) {
+          $entity_type = $object->{$this->aliases['entity_type']};
+          if (empty($this->definition['is revision'])) {
+            $entity_id = $object->{$this->field_alias};
+            $entities_by_type[$entity_type][$key] = $entity_id;
+          }
+          else {
+            $revision_id = $object->{$this->field_alias};
+            $entity_id = $object->{$this->aliases['entity_id']};
+            $entities_by_type[$entity_type][$key] = array($entity_id, $revision_id);
+          }
+        }
+      }
+
+      // Load the entities.
+      foreach ($entities_by_type as $entity_type => $entity_ids) {
+        $entity_info = entity_get_info($entity_type);
+        if (empty($this->definition['is revision'])) {
+          $entities = entity_load($entity_type, $entity_ids);
+          $keys = $entity_ids;
+        }
+        else {
+          // Revisions can't be loaded multiple, so we have to load them
+          // one by one.
+          $entities = array();
+          $keys = array();
+          foreach ($entity_ids as $key => $combined) {
+            list($entity_id, $revision_id) = $combined;
+            $entity = entity_load($entity_type, array($entity_id), array($entity_info['entity keys']['revision'] => $revision_id));
+            if ($entity) {
+              $entities[$revision_id] = array_shift($entity);
+              $keys[$key] = $revision_id;
+            }
+          }
+        }
+
+        foreach ($keys as $key => $entity_id) {
+          // If this is a revision, load the revision instead.
+          if (isset($entities[$entity_id])) {
+            $values[$key]->_field_data[$this->field_alias] = array(
+              'entity_type' => $entity_type,
+              'entity' => $entities[$entity_id],
+            );
+          }
+        }
+      }
+
+      // Now, transfer the data back into the resultset so it can be easily used.
+      foreach ($values as $row_id => &$value) {
+        $value->{'field_' . $this->options['id']} = $this->set_items($value, $row_id);
+      }
+    }
+  }
+
+  /**
+   * Render all items in this field together.
+   *
+   * When using advanced render, each possible item in the list is rendered
+   * individually. Then the items are all pasted together.
+   */
+  function render_items($items) {
+    if (!empty($items)) {
+      if (!$this->options['group_rows']) {
+        return implode('', $items);
+      }
+
+      if ($this->options['multi_type'] == 'separator') {
+        return implode(filter_xss_admin($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', array(
+          'items' => $items,
+          'title' => NULL,
+          'type' => $this->options['multi_type'],
+        ));
+      }
+    }
+  }
+
+  function get_items($values) {
+    return $values->{'field_' . $this->options['id']};
+  }
+
+  function get_value($values, $field = NULL) {
+    // Go ahead and render and store in $this->items.
+
+    // Deep clone needed, otherwise over written by $new_values.
+    $entity = $values->_field_data[$this->field_alias]['entity'];
+    $entity = unserialize(serialize($entity));
+
+    $entity_type = $values->_field_data[$this->field_alias]['entity_type'];
+    $langcode = $this->field_language($entity_type, $entity);
+    if (empty($langcode)) {
+      $langcode = LANGUAGE_NONE;
+    }
+
+    // If we are grouping, copy our group fields into the cloned entity.
+    // It's possible this will cause some weirdness, but there's only
+    // so much we can hope to do.
+    if (!empty($this->group_fields)) {
+      // first, test to see if we have a base value.
+      $base_value = array();
+      // Note: We would copy original values here, but it can cause problems.
+      // For example, text fields store cached filtered values as
+      // 'safe_value' which doesn't appear anywhere in the field definition
+      // so we can't affect it. Other side effects could happen similarly.
+      $data = FALSE;
+      foreach ($this->group_fields as $field_name => $column) {
+        if (property_exists($values, $this->aliases[$column])) {
+          $base_value[$field_name] = $values->{$this->aliases[$column]};
+          if (isset($base_value[$field_name])) {
+            $data = TRUE;
+          }
+        }
+      }
+
+      // If any of our aggregated fields have data, fake it:
+      if ($data) {
+        // Now, overwrite the original value with our aggregated value.
+        // This overwrites it so there is always just one entry.
+        $entity->{$this->definition['field_name']}[$langcode] = array($base_value);
+      }
+      else {
+        $entity->{$this->definition['field_name']}[$langcode] = array();
+      }
+    }
+
+    // The field we are trying to display doesn't exist on this entity.
+    if (!isset($entity->{$this->definition['field_name']})) {
+      return array();
+    }
+
+    // We are supposed to show only certain deltas.
+    if ($this->limit_values && !empty($entity->{$this->definition['field_name']})) {
+      $all_values = !empty($entity->{$this->definition['field_name']}[$langcode]) ? $entity->{$this->definition['field_name']}[$langcode] : array();
+      if ($this->options['delta_reversed']) {
+        $all_values = array_reverse($all_values);
+      }
+
+      // Offset is calculated differently when row grouping for a field is
+      // not enabled. Since there are multiple rows, the delta needs to be
+      // taken into account, so that different values are shown per row.
+      if (!$this->options['group_rows'] && isset($this->aliases['delta']) && isset($values->{$this->aliases['delta']})) {
+        $delta_limit = 1;
+        $offset = $values->{$this->aliases['delta']};
+      }
+      // Single fields don't have a delta available so choose 0.
+      elseif (!$this->options['group_rows'] && !$this->multiple) {
+        $delta_limit = 1;
+        $offset = 0;
+      }
+      else {
+        $delta_limit = $this->options['delta_limit'];
+        $offset = intval($this->options['delta_offset']);
+
+        // We should only get here in this case if there's an offset, and
+        // in that case we're limiting to all values after the offset.
+        if ($delta_limit == 'all') {
+          $delta_limit = count($all_values) - $offset;
+        }
+      }
+
+      // Determine if only the first and last values should be shown
+      $delta_first_last = $this->options['delta_first_last'];
+
+      $new_values = array();
+      for ($i = 0; $i < $delta_limit; $i++) {
+        $new_delta = $offset + $i;
+
+        if (isset($all_values[$new_delta])) {
+          // If first-last option was selected, only use the first and last values
+          if (!$delta_first_last
+          // Use the first value.
+           || $new_delta == $offset
+          // Use the last value.
+           || $new_delta == ($delta_limit + $offset - 1)) {
+            $new_values[] = $all_values[$new_delta];
+          }
+        }
+      }
+      $entity->{$this->definition['field_name']}[$langcode] = $new_values;
+    }
+
+    if ($field == 'entity') {
+      return $entity;
+    }
+    else {
+      return !empty($entity->{$this->definition['field_name']}[$langcode]) ? $entity->{$this->definition['field_name']}[$langcode] : array();
+    }
+  }
+
+  /**
+   * Return an array of items for the field.
+   */
+  function set_items($values, $row_id) {
+    // In some cases the instance on the entity might be easy, see
+    // https://drupal.org/node/1161708 and https://drupal.org/node/1461536 for
+    // more information.
+    if (empty($values->_field_data[$this->field_alias]) || empty($values->_field_data[$this->field_alias]['entity']) || !isset($values->_field_data[$this->field_alias]['entity']->{$this->definition['field_name']})) {
+      return array();
+    }
+
+    $display = array(
+      'type' => $this->options['type'],
+      'settings' => $this->options['settings'],
+      'label' => 'hidden',
+
+      // Pass the View object in the display so that fields can act on it.
+      'views_view' => $this->view,
+      'views_field' => $this,
+      'views_row_id' => $row_id,
+    );
+
+
+    $entity_type = $values->_field_data[$this->field_alias]['entity_type'];
+    $entity = $this->get_value($values, 'entity');
+    if (!$entity) {
+      return array();
+    }
+
+    $langcode = $this->field_language($entity_type, $entity);
+    if (empty($langcode)) {
+      $langcode = LANGUAGE_NONE;
+    }
+
+    $multifield_items = field_get_items($entity_type, $entity, $this->definition['field_name'], $langcode);
+
+    if (is_array($multifield_items)) {
+      array_walk($multifield_items, 'multifield_item_unserialize', multifield_extract_multifield_machine_name($this->multifield_info));
+    }
+    else {
+      $multifield_items = array();
+    }
+
+    $render_array = array();
+    foreach ($multifield_items as $multifield_item) {
+      $multifield = _multifield_field_item_to_entity(multifield_extract_multifield_machine_name($this->multifield_info), $multifield_item);
+      $subfield_langcode = $this->field_language('multifield', $multifield);
+      if (empty($render_array)) {
+        $render_array = field_view_field('multifield', $multifield, $this->definition['subfield_name'], $display, $subfield_langcode);
+      }
+      else {
+        $subfield_render_array = field_view_field('multifield', $multifield, $this->definition['subfield_name'], $display, $subfield_langcode);
+        // Multifield subfields are always single value.
+        $render_array[] = $subfield_render_array[0];
+      }
+    }
+
+    $items = array();
+    if ($this->options['field_api_classes']) {
+      // Make a copy.
+      $array = $render_array;
+      return array(array('rendered' => drupal_render($render_array)));
+    }
+
+    foreach (element_children($render_array) as $count) {
+      $items[$count]['rendered'] = $render_array[$count];
+      // field_view_field() adds an #access property to the render array that
+      // determines whether or not the current user is allowed to view the
+      // field in the context of the current entity. We need to respect this
+      // parameter when we pull out the children of the field array for
+      // rendering.
+      if (isset($render_array['#access'])) {
+        $items[$count]['rendered']['#access'] = $render_array['#access'];
+      }
+      // Only add the raw field items (for use in tokens) if the current user
+      // has access to view the field content.
+      if ((!isset($items[$count]['rendered']['#access']) || $items[$count]['rendered']['#access']) && !empty($render_array['#items'][$count])) {
+        $items[$count]['raw'] = $render_array['#items'][$count];
+      }
+    }
+    return $items;
+  }
+
+  function render_item($count, $item) {
+    return render($item['rendered']);
+  }
+
+  function document_self_tokens(&$tokens) {
+    if ($this->fieldHandler) {
+      $this->fieldHandler->document_self_tokens($tokens);
+    }
+  }
+
+  function add_self_tokens(&$tokens, $item) {
+    if ($this->fieldHandler) {
+      $this->fieldHandler->add_self_tokens($tokens, $item);
+    }
+  }
+
+  public function __call($name, $arguments) {
+    return call_user_func_array(array($this->fieldHandler, $name), $arguments);
+  }
+}
diff --git a/multifield.info b/multifield.info
index 2593411..dcc3df1
--- a/multifield.info
+++ b/multifield.info
@@ -6,6 +6,7 @@ dependencies[] = ctools
 dependencies[] = field
 configure = admin/structure/multifield
 files[] = MultifieldEntityController.php
+files[] = MultifieldViewsHandler.php
 files[] = tests/MultifieldAdministrationTestCase.test
 files[] = tests/MultifieldCommerceIntegrationTest.test
 files[] = tests/MultifieldDevelGenerateTestCase.test
diff --git a/multifield.module b/multifield.module
index 1f12d3d..8645234
--- a/multifield.module
+++ b/multifield.module
@@ -4,6 +4,17 @@ require_once dirname(__FILE__) . '/multifield.field.inc';
 require_once dirname(__FILE__) . '/multifield.features.inc';
 
 /**
+ * Implements hook_hook_info_alter().
+ */
+function multifield_hook_info_alter(&$info) {
+  // @todo Remove when https://www.drupal.org/node/2309543 is fixed.
+  $info += array_fill_keys(array(
+    'field_views_data',
+    'field_views_data_alter',
+  ), array('group' => 'views'));
+}
+
+/**
  * Implements hook_permission().
  */
 function multifield_permission() {
@@ -101,12 +112,14 @@ function multifield_entity_info() {
     'label' => t('Multifield'),
     'controller class' => 'MultifieldEntityController',
     'base table' => 'multifield',
+    'revision table' => 'multifield',
     'fieldable' => TRUE,
     // Mark this as a configuration entity type to prevent other modules from
     // assuming they can do stuff with this entity type.
     'configuration' => TRUE,
     'bundle keys' => array(
       'bundle' => 'machine_name',
+      'revision' => 'revision_id',
     ),
     'entity keys' => array(
       'id' => 'id',
@@ -602,20 +615,6 @@ function multifield_form_field_ui_field_delete_form_alter(&$form, &$form_state)
 }
 
 /**
- * Implements hook_views_data_alter().
- */
-function multifield_views_data_alter(array &$data) {
-  // Remove any references to the fake multifield table.
-  unset($data['multifield']);
-  unset($data['entity_multifield']);
-  unset($data['views_entity_multifield']);
-  foreach ($data as &$table) {
-    unset($table['table']['join']['multifield']);
-    unset($table['table']['default_relationship']['multifield']);
-  }
-}
-
-/**
  * Implements hook_admin_menu_map().
  */
 function multifield_admin_menu_map() {
diff --git a/multifield.views.inc b/multifield.views.inc
index 0000000..08d886b
--- /dev/null
+++ b/multifield.views.inc
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * Implements hook_views_data_alter().
+ */
+function multifield_views_data_alter(array &$data) {
+  // Remove any references to the fake multifield table.
+  unset($data['multifield']);
+  unset($data['entity_multifield']);
+  unset($data['views_entity_multifield']);
+  foreach ($data as &$table) {
+    unset($table['table']['join']['multifield']);
+    unset($table['table']['default_relationship']['multifield']);
+  }
+}
+
+/**
+ * Implements hook_field_views_data().
+ */
+function multifield_field_views_data($field) {
+  $field_name = $field['field_name'];
+  $machine_name = multifield_extract_multifield_machine_name($field);
+  $subfield_names = multifield_type_get_subfields($machine_name);
+  $data = field_views_field_default_views_data($field);
+  $all_subfield_data = _multifield_subfield_views_data($field);
+
+  foreach ($data as &$table) {
+    if (isset($table[$field_name]['field'])) {
+      foreach ($subfield_names as $subfield_name) {
+        $subfield = field_info_field($subfield_name);
+        $subfield_data = $all_subfield_data[$subfield_name];
+        $new_name = $field_name . '_' . $subfield_name;
+        $f_data = $subfield_data['field_data_' . $subfield_name][$subfield_name];
+        $f_data['table'] = $table[$field_name]['field']['table'];
+        $f_data['field']['table'] = $table[$field_name]['field']['table'];
+        $f_data['field']['entity_tables'] = $table[$field_name]['field']['entity_tables'];
+        foreach ($f_data['field']['additional fields'] as $index => &$_additional) {
+          if (!in_array($_additional, array('delta', 'language', 'bundle'))) {
+            $_additional = $field_name . '_' . $_additional;
+          }
+        }
+        foreach ($f_data['field']['additional fields'] as $index => $additional) {
+          if (!in_array($additional, array('delta', 'language', 'bundle'))) {
+            $source_field = substr($f_data['field']['additional fields'][$index], strlen($field_name . '_'));
+            if (($key = array_search($f_data['field']['additional fields'][$index], $table[$field_name]['field']['additional fields'])) !== false) {
+              unset($table[$field_name]['field']['additional fields'][$f_data['field']['additional fields'][$index]]);
+            }
+            $table[$f_data['field']['additional fields'][$index]]['field'] = $f_data['field'];
+            $table[$f_data['field']['additional fields'][$index]]['field']['handler'] = 'MultifieldViewsHandler';
+            $table[$f_data['field']['additional fields'][$index]]['field']['subfield_name'] = $f_data['field']['field_name'];
+            $table[$f_data['field']['additional fields'][$index]]['field']['field_name'] = $field_name;
+            if (isset($subfield_data['field_data_' . $subfield_name][$source_field]['field'])) {
+              $table[$f_data['field']['additional fields'][$index]]['field']['subfield handler'] = $subfield_data['field_data_' . $subfield_name][$source_field]['field']['handler'];
+            }
+            else {
+              $table[$f_data['field']['additional fields'][$index]]['field']['subfield handler'] = $f_data['field']['handler'];
+            }
+            foreach (array('argument', 'filter', 'sort') as $handler) {
+              if (isset($subfield_data['field_data_' . $subfield_name][$source_field][$handler]) && isset($table[$f_data['field']['additional fields'][$index]][$handler])) {
+                // Overwrite field_name.
+                $table[$f_data['field']['additional fields'][$index]][$handler]['field_name'] = $subfield_data['field_data_' . $subfield_name][$source_field][$handler]['field_name'];
+                // Overwrite handler.
+                $table[$f_data['field']['additional fields'][$index]][$handler]['handler'] = $subfield_data['field_data_' . $subfield_name][$source_field][$handler]['handler'];
+                // Add additional options without overwriting table etc.
+                $table[$f_data['field']['additional fields'][$index]][$handler] += $subfield_data['field_data_' . $subfield_name][$source_field][$handler];
+              }
+            }
+            if (isset($subfield_data['field_data_' . $subfield_name][$source_field]['relationship'])) {
+              $table[$f_data['field']['additional fields'][$index]]['relationship'] = $subfield_data['field_data_' . $subfield_name][$source_field]['relationship'];
+            }
+          }
+        }
+        $table[$new_name] = $f_data;
+        $table[$new_name]['field']['handler'] = 'MultifieldViewsHandler';
+        $table[$new_name]['field']['subfield_name'] = $f_data['field']['field_name'];
+        $table[$new_name]['field']['field_name'] = $field_name;
+        $table[$new_name]['field']['subfield handler'] = $f_data['field']['handler'];
+      }
+    }
+  }
+  return $data;
+}
+
+function _multifield_subfield_views_data($field) {
+  $data = array();
+  $machine_name = multifield_extract_multifield_machine_name($field);
+  foreach (multifield_type_get_subfields($machine_name) as $subfield_name) {
+    $subfield = field_info_field($subfield_name);
+    if ($subfield['storage']['type'] != 'field_sql_storage') {
+      continue;
+    }
+
+    $result = (array) module_invoke($subfield['module'], 'field_views_data', $subfield);
+
+    if (empty($result)) {
+      $result = field_views_field_default_views_data($subfield);
+    }
+    drupal_alter('field_views_data', $result, $subfield, $subfield['module']);
+    $data[$subfield_name] = $result;
+  }
+
+  return $data;
+}
