cms/drupal/modules/field/field.info.class.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * @file
       
     5  * Definition of the FieldInfo class.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Provides field and instance definitions for the current runtime environment.
       
    10  *
       
    11  * A FieldInfo object is created and statically persisted through the request
       
    12  * by the _field_info_field_cache() function. The object properties act as a
       
    13  * "static cache" of fields and instances definitions.
       
    14  *
       
    15  * The preferred way to access definitions is through the getBundleInstances()
       
    16  * method, which keeps cache entries per bundle, storing both fields and
       
    17  * instances for a given bundle. Fields used in multiple bundles are duplicated
       
    18  * in several cache entries, and are merged into a single list in the memory
       
    19  * cache. Cache entries are loaded for bundles as a whole, optimizing memory
       
    20  * and CPU usage for the most common pattern of iterating over all instances of
       
    21  * a bundle rather than accessing a single instance.
       
    22  *
       
    23  * The getFields() and getInstances() methods, which return all existing field
       
    24  * and instance definitions, are kept mainly for backwards compatibility, and
       
    25  * should be avoided when possible, since they load and persist in memory a
       
    26  * potentially large array of information. In many cases, the lightweight
       
    27  * getFieldMap() method should be preferred.
       
    28  */
       
    29 class FieldInfo {
       
    30 
       
    31   /**
       
    32    * Lightweight map of fields across entity types and bundles.
       
    33    *
       
    34    * @var array
       
    35    */
       
    36   protected $fieldMap;
       
    37 
       
    38   /**
       
    39    * List of $field structures keyed by ID. Includes deleted fields.
       
    40    *
       
    41    * @var array
       
    42    */
       
    43   protected $fieldsById = array();
       
    44 
       
    45   /**
       
    46    * Mapping of field names to the ID of the corresponding non-deleted field.
       
    47    *
       
    48    * @var array
       
    49    */
       
    50   protected $fieldIdsByName = array();
       
    51 
       
    52   /**
       
    53    * Whether $fieldsById contains all field definitions or a subset.
       
    54    *
       
    55    * @var bool
       
    56    */
       
    57   protected $loadedAllFields = FALSE;
       
    58 
       
    59   /**
       
    60    * Separately tracks requested field names or IDs that do not exist.
       
    61    *
       
    62    * @var array
       
    63    */
       
    64   protected $unknownFields = array();
       
    65 
       
    66   /**
       
    67    * Instance definitions by bundle.
       
    68    *
       
    69    * @var array
       
    70    */
       
    71   protected $bundleInstances = array();
       
    72 
       
    73   /**
       
    74    * Whether $bundleInstances contains all instances definitions or a subset.
       
    75    *
       
    76    * @var bool
       
    77    */
       
    78   protected $loadedAllInstances = FALSE;
       
    79 
       
    80   /**
       
    81    * Separately tracks requested bundles that are empty (or do not exist).
       
    82    *
       
    83    * @var array
       
    84    */
       
    85   protected $emptyBundles = array();
       
    86 
       
    87   /**
       
    88    * Extra fields by bundle.
       
    89    *
       
    90    * @var array
       
    91    */
       
    92   protected $bundleExtraFields = array();
       
    93 
       
    94   /**
       
    95    * Clears the "static" and persistent caches.
       
    96    */
       
    97   public function flush() {
       
    98     $this->fieldMap = NULL;
       
    99 
       
   100     $this->fieldsById = array();
       
   101     $this->fieldIdsByName = array();
       
   102     $this->loadedAllFields = FALSE;
       
   103     $this->unknownFields = array();
       
   104 
       
   105     $this->bundleInstances = array();
       
   106     $this->loadedAllInstances = FALSE;
       
   107     $this->emptyBundles = array();
       
   108 
       
   109     $this->bundleExtraFields = array();
       
   110 
       
   111     cache_clear_all('field_info:', 'cache_field', TRUE);
       
   112   }
       
   113 
       
   114   /**
       
   115    * Collects a lightweight map of fields across bundles.
       
   116    *
       
   117    * @return
       
   118    *   An array keyed by field name. Each value is an array with two entries:
       
   119    *   - type: The field type.
       
   120    *   - bundles: The bundles in which the field appears, as an array with
       
   121    *     entity types as keys and the array of bundle names as values.
       
   122    */
       
   123   public function getFieldMap() {
       
   124     // Read from the "static" cache.
       
   125     if ($this->fieldMap !== NULL) {
       
   126       return $this->fieldMap;
       
   127     }
       
   128 
       
   129     // Read from persistent cache.
       
   130     if ($cached = cache_get('field_info:field_map', 'cache_field')) {
       
   131       $map = $cached->data;
       
   132 
       
   133       // Save in "static" cache.
       
   134       $this->fieldMap = $map;
       
   135 
       
   136       return $map;
       
   137     }
       
   138 
       
   139     $map = array();
       
   140 
       
   141     $query = db_query('SELECT fc.type, fci.field_name, fci.entity_type, fci.bundle FROM {field_config_instance} fci INNER JOIN {field_config} fc ON fc.id = fci.field_id WHERE fc.active = 1 AND fc.storage_active = 1 AND fc.deleted = 0 AND fci.deleted = 0');
       
   142     foreach ($query as $row) {
       
   143       $map[$row->field_name]['bundles'][$row->entity_type][] = $row->bundle;
       
   144       $map[$row->field_name]['type'] = $row->type;
       
   145     }
       
   146 
       
   147     // Save in "static" and persistent caches.
       
   148     $this->fieldMap = $map;
       
   149     if (lock_acquire('field_info:field_map')) {
       
   150       cache_set('field_info:field_map', $map, 'cache_field');
       
   151       lock_release('field_info:field_map');
       
   152     }
       
   153 
       
   154     return $map;
       
   155   }
       
   156 
       
   157   /**
       
   158    * Returns all active fields, including deleted ones.
       
   159    *
       
   160    * @return
       
   161    *   An array of field definitions, keyed by field ID.
       
   162    */
       
   163   public function getFields() {
       
   164     // Read from the "static" cache.
       
   165     if ($this->loadedAllFields) {
       
   166       return $this->fieldsById;
       
   167     }
       
   168 
       
   169     // Read from persistent cache.
       
   170     if ($cached = cache_get('field_info:fields', 'cache_field')) {
       
   171       $this->fieldsById = $cached->data;
       
   172     }
       
   173     else {
       
   174       // Collect and prepare fields.
       
   175       foreach (field_read_fields(array(), array('include_deleted' => TRUE)) as $field) {
       
   176         $this->fieldsById[$field['id']] = $this->prepareField($field);
       
   177       }
       
   178 
       
   179       // Store in persistent cache.
       
   180       if (lock_acquire('field_info:fields')) {
       
   181         cache_set('field_info:fields', $this->fieldsById, 'cache_field');
       
   182         lock_release('field_info:fields');
       
   183       }
       
   184     }
       
   185 
       
   186     // Fill the name/ID map.
       
   187     foreach ($this->fieldsById as $field) {
       
   188       if (!$field['deleted']) {
       
   189         $this->fieldIdsByName[$field['field_name']] = $field['id'];
       
   190       }
       
   191     }
       
   192 
       
   193     $this->loadedAllFields = TRUE;
       
   194 
       
   195     return $this->fieldsById;
       
   196   }
       
   197 
       
   198   /**
       
   199    * Retrieves all active, non-deleted instances definitions.
       
   200    *
       
   201    * @param $entity_type
       
   202    *   (optional) The entity type.
       
   203    *
       
   204    * @return
       
   205    *   If $entity_type is not set, all instances keyed by entity type and bundle
       
   206    *   name. If $entity_type is set, all instances for that entity type, keyed
       
   207    *   by bundle name.
       
   208    */
       
   209   public function getInstances($entity_type = NULL) {
       
   210     // If the full list is not present in "static" cache yet.
       
   211     if (!$this->loadedAllInstances) {
       
   212 
       
   213       // Read from persistent cache.
       
   214       if ($cached = cache_get('field_info:instances', 'cache_field')) {
       
   215         $this->bundleInstances = $cached->data;
       
   216       }
       
   217       else {
       
   218         // Collect and prepare instances.
       
   219 
       
   220         // We also need to populate the static field cache, since it will not
       
   221         // be set by subsequent getBundleInstances() calls.
       
   222         $this->getFields();
       
   223 
       
   224         // Initialize empty arrays for all existing entity types and bundles.
       
   225         // This is not strictly needed, but is done to preserve the behavior of
       
   226         // field_info_instances() before http://drupal.org/node/1915646.
       
   227         foreach (field_info_bundles() as $existing_entity_type => $bundles) {
       
   228           foreach ($bundles as $bundle => $bundle_info) {
       
   229             $this->bundleInstances[$existing_entity_type][$bundle] = array();
       
   230           }
       
   231         }
       
   232 
       
   233         foreach (field_read_instances() as $instance) {
       
   234           $field = $this->getField($instance['field_name']);
       
   235           $instance = $this->prepareInstance($instance, $field['type']);
       
   236           $this->bundleInstances[$instance['entity_type']][$instance['bundle']][$instance['field_name']] = $instance;
       
   237         }
       
   238 
       
   239         // Store in persistent cache.
       
   240         if (lock_acquire('field_info:instances')) {
       
   241           cache_set('field_info:instances', $this->bundleInstances, 'cache_field');
       
   242           lock_release('field_info:instances');
       
   243         }
       
   244       }
       
   245 
       
   246       $this->loadedAllInstances = TRUE;
       
   247     }
       
   248 
       
   249     if (isset($entity_type)) {
       
   250       return isset($this->bundleInstances[$entity_type]) ? $this->bundleInstances[$entity_type] : array();
       
   251     }
       
   252     else {
       
   253       return $this->bundleInstances;
       
   254     }
       
   255   }
       
   256 
       
   257   /**
       
   258    * Returns a field definition from a field name.
       
   259    *
       
   260    * This method only retrieves active, non-deleted fields.
       
   261    *
       
   262    * @param $field_name
       
   263    *   The field name.
       
   264    *
       
   265    * @return
       
   266    *   The field definition, or NULL if no field was found.
       
   267    */
       
   268   public function getField($field_name) {
       
   269     // Read from the "static" cache.
       
   270     if (isset($this->fieldIdsByName[$field_name])) {
       
   271       $field_id = $this->fieldIdsByName[$field_name];
       
   272       return $this->fieldsById[$field_id];
       
   273     }
       
   274     if (isset($this->unknownFields[$field_name])) {
       
   275       return;
       
   276     }
       
   277 
       
   278     // Do not check the (large) persistent cache, but read the definition.
       
   279 
       
   280     // Cache miss: read from definition.
       
   281     if ($field = field_read_field($field_name)) {
       
   282       $field = $this->prepareField($field);
       
   283 
       
   284       // Save in the "static" cache.
       
   285       $this->fieldsById[$field['id']] = $field;
       
   286       $this->fieldIdsByName[$field['field_name']] = $field['id'];
       
   287 
       
   288       return $field;
       
   289     }
       
   290     else {
       
   291       $this->unknownFields[$field_name] = TRUE;
       
   292     }
       
   293   }
       
   294 
       
   295   /**
       
   296    * Returns a field definition from a field ID.
       
   297    *
       
   298    * This method only retrieves active fields, deleted or not.
       
   299    *
       
   300    * @param $field_id
       
   301    *   The field ID.
       
   302    *
       
   303    * @return
       
   304    *   The field definition, or NULL if no field was found.
       
   305    */
       
   306   public function getFieldById($field_id) {
       
   307     // Read from the "static" cache.
       
   308     if (isset($this->fieldsById[$field_id])) {
       
   309       return $this->fieldsById[$field_id];
       
   310     }
       
   311     if (isset($this->unknownFields[$field_id])) {
       
   312       return;
       
   313     }
       
   314 
       
   315     // No persistent cache, fields are only persistently cached as part of a
       
   316     // bundle.
       
   317 
       
   318     // Cache miss: read from definition.
       
   319     if ($fields = field_read_fields(array('id' => $field_id), array('include_deleted' => TRUE))) {
       
   320       $field = current($fields);
       
   321       $field = $this->prepareField($field);
       
   322 
       
   323       // Store in the static cache.
       
   324       $this->fieldsById[$field['id']] = $field;
       
   325       if (!$field['deleted']) {
       
   326         $this->fieldIdsByName[$field['field_name']] = $field['id'];
       
   327       }
       
   328 
       
   329       return $field;
       
   330     }
       
   331     else {
       
   332       $this->unknownFields[$field_id] = TRUE;
       
   333     }
       
   334   }
       
   335 
       
   336   /**
       
   337    * Retrieves the instances for a bundle.
       
   338    *
       
   339    * The function also populates the corresponding field definitions in the
       
   340    * "static" cache.
       
   341    *
       
   342    * @param $entity_type
       
   343    *   The entity type.
       
   344    * @param $bundle
       
   345    *   The bundle name.
       
   346    *
       
   347    * @return
       
   348    *   The array of instance definitions, keyed by field name.
       
   349    */
       
   350   public function getBundleInstances($entity_type, $bundle) {
       
   351     // Read from the "static" cache.
       
   352     if (isset($this->bundleInstances[$entity_type][$bundle])) {
       
   353       return $this->bundleInstances[$entity_type][$bundle];
       
   354     }
       
   355     if (isset($this->emptyBundles[$entity_type][$bundle])) {
       
   356       return array();
       
   357     }
       
   358 
       
   359     // Read from the persistent cache.
       
   360     if ($cached = cache_get("field_info:bundle:$entity_type:$bundle", 'cache_field')) {
       
   361       $info = $cached->data;
       
   362 
       
   363       // Extract the field definitions and save them in the "static" cache.
       
   364       foreach ($info['fields'] as $field) {
       
   365         if (!isset($this->fieldsById[$field['id']])) {
       
   366           $this->fieldsById[$field['id']] = $field;
       
   367           if (!$field['deleted']) {
       
   368             $this->fieldIdsByName[$field['field_name']] = $field['id'];
       
   369           }
       
   370         }
       
   371       }
       
   372       unset($info['fields']);
       
   373 
       
   374       // Store the instance definitions in the "static" cache'. Empty (or
       
   375       // non-existent) bundles are stored separately, so that they do not
       
   376       // pollute the global list returned by getInstances().
       
   377       if ($info['instances']) {
       
   378         $this->bundleInstances[$entity_type][$bundle] = $info['instances'];
       
   379       }
       
   380       else {
       
   381         $this->emptyBundles[$entity_type][$bundle] = TRUE;
       
   382       }
       
   383 
       
   384       return $info['instances'];
       
   385     }
       
   386 
       
   387     // Cache miss: collect from the definitions.
       
   388 
       
   389     $instances = array();
       
   390 
       
   391     // Collect the fields in the bundle.
       
   392     $params = array('entity_type' => $entity_type, 'bundle' => $bundle);
       
   393     $fields = field_read_fields($params);
       
   394 
       
   395     // This iterates on non-deleted instances, so deleted fields are kept out of
       
   396     // the persistent caches.
       
   397     foreach (field_read_instances($params) as $instance) {
       
   398       $field = $fields[$instance['field_name']];
       
   399 
       
   400       $instance = $this->prepareInstance($instance, $field['type']);
       
   401       $instances[$field['field_name']] = $instance;
       
   402 
       
   403       // If the field is not in our global "static" list yet, add it.
       
   404       if (!isset($this->fieldsById[$field['id']])) {
       
   405         $field = $this->prepareField($field);
       
   406 
       
   407         $this->fieldsById[$field['id']] = $field;
       
   408         $this->fieldIdsByName[$field['field_name']] = $field['id'];
       
   409       }
       
   410     }
       
   411 
       
   412     // Store in the 'static' cache'. Empty (or non-existent) bundles are stored
       
   413     // separately, so that they do not pollute the global list returned by
       
   414     // getInstances().
       
   415     if ($instances) {
       
   416       $this->bundleInstances[$entity_type][$bundle] = $instances;
       
   417     }
       
   418     else {
       
   419       $this->emptyBundles[$entity_type][$bundle] = TRUE;
       
   420     }
       
   421 
       
   422     // The persistent cache additionally contains the definitions of the fields
       
   423     // involved in the bundle.
       
   424     $cache = array(
       
   425       'instances' => $instances,
       
   426       'fields' => array()
       
   427     );
       
   428     foreach ($instances as $instance) {
       
   429       $cache['fields'][] = $this->fieldsById[$instance['field_id']];
       
   430     }
       
   431 
       
   432     if (lock_acquire("field_info:bundle:$entity_type:$bundle")) {
       
   433       cache_set("field_info:bundle:$entity_type:$bundle", $cache, 'cache_field');
       
   434       lock_release("field_info:bundle:$entity_type:$bundle");
       
   435     }
       
   436 
       
   437     return $instances;
       
   438   }
       
   439 
       
   440   /**
       
   441    * Retrieves the "extra fields" for a bundle.
       
   442    *
       
   443    * @param $entity_type
       
   444    *   The entity type.
       
   445    * @param $bundle
       
   446    *   The bundle name.
       
   447    *
       
   448    * @return
       
   449    *   The array of extra fields.
       
   450    */
       
   451   public function getBundleExtraFields($entity_type, $bundle) {
       
   452     // Read from the "static" cache.
       
   453     if (isset($this->bundleExtraFields[$entity_type][$bundle])) {
       
   454       return $this->bundleExtraFields[$entity_type][$bundle];
       
   455     }
       
   456 
       
   457     // Read from the persistent cache.
       
   458     if ($cached = cache_get("field_info:bundle_extra:$entity_type:$bundle", 'cache_field')) {
       
   459       $this->bundleExtraFields[$entity_type][$bundle] = $cached->data;
       
   460       return $this->bundleExtraFields[$entity_type][$bundle];
       
   461     }
       
   462 
       
   463     // Cache miss: read from hook_field_extra_fields(). Note: given the current
       
   464     // shape of the hook, we have no other way than collecting extra fields on
       
   465     // all bundles.
       
   466     $info = array();
       
   467     $extra = module_invoke_all('field_extra_fields');
       
   468     drupal_alter('field_extra_fields', $extra);
       
   469     // Merge in saved settings.
       
   470     if (isset($extra[$entity_type][$bundle])) {
       
   471       $info = $this->prepareExtraFields($extra[$entity_type][$bundle], $entity_type, $bundle);
       
   472     }
       
   473 
       
   474     // Store in the 'static' and persistent caches.
       
   475     $this->bundleExtraFields[$entity_type][$bundle] = $info;
       
   476     if (lock_acquire("field_info:bundle_extra:$entity_type:$bundle")) {
       
   477       cache_set("field_info:bundle_extra:$entity_type:$bundle", $info, 'cache_field');
       
   478       lock_release("field_info:bundle_extra:$entity_type:$bundle");
       
   479     }
       
   480 
       
   481     return $this->bundleExtraFields[$entity_type][$bundle];
       
   482   }
       
   483 
       
   484   /**
       
   485    * Prepares a field definition for the current run-time context.
       
   486    *
       
   487    * @param $field
       
   488    *   The raw field structure as read from the database.
       
   489    *
       
   490    * @return
       
   491    *   The field definition completed for the current runtime context.
       
   492    */
       
   493   public function prepareField($field) {
       
   494     // Make sure all expected field settings are present.
       
   495     $field['settings'] += field_info_field_settings($field['type']);
       
   496     $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
       
   497 
       
   498     // Add storage details.
       
   499     $details = (array) module_invoke($field['storage']['module'], 'field_storage_details', $field);
       
   500     drupal_alter('field_storage_details', $details, $field);
       
   501     $field['storage']['details'] = $details;
       
   502 
       
   503     // Populate the list of bundles using the field.
       
   504     $field['bundles'] = array();
       
   505     if (!$field['deleted']) {
       
   506       $map = $this->getFieldMap();
       
   507       if (isset($map[$field['field_name']])) {
       
   508         $field['bundles'] = $map[$field['field_name']]['bundles'];
       
   509       }
       
   510     }
       
   511 
       
   512     return $field;
       
   513   }
       
   514 
       
   515   /**
       
   516    * Prepares an instance definition for the current run-time context.
       
   517    *
       
   518    * @param $instance
       
   519    *   The raw instance structure as read from the database.
       
   520    * @param $field_type
       
   521    *   The field type.
       
   522    *
       
   523    * @return
       
   524    *   The field instance array completed for the current runtime context.
       
   525    */
       
   526   public function prepareInstance($instance, $field_type) {
       
   527     // Make sure all expected instance settings are present.
       
   528     $instance['settings'] += field_info_instance_settings($field_type);
       
   529 
       
   530     // Set a default value for the instance.
       
   531     if (field_behaviors_widget('default value', $instance) == FIELD_BEHAVIOR_DEFAULT && !isset($instance['default_value'])) {
       
   532       $instance['default_value'] = NULL;
       
   533     }
       
   534 
       
   535     // Prepare widget settings.
       
   536     $instance['widget'] = $this->prepareInstanceWidget($instance['widget'], $field_type);
       
   537 
       
   538     // Prepare display settings.
       
   539     foreach ($instance['display'] as $view_mode => $display) {
       
   540       $instance['display'][$view_mode] = $this->prepareInstanceDisplay($display, $field_type);
       
   541     }
       
   542 
       
   543     // Fall back to 'hidden' for view modes configured to use custom display
       
   544     // settings, and for which the instance has no explicit settings.
       
   545     $entity_info = entity_get_info($instance['entity_type']);
       
   546     $view_modes = array_merge(array('default'), array_keys($entity_info['view modes']));
       
   547     $view_mode_settings = field_view_mode_settings($instance['entity_type'], $instance['bundle']);
       
   548     foreach ($view_modes as $view_mode) {
       
   549       if ($view_mode == 'default' || !empty($view_mode_settings[$view_mode]['custom_settings'])) {
       
   550         if (!isset($instance['display'][$view_mode])) {
       
   551           $instance['display'][$view_mode] = array(
       
   552             'type' => 'hidden',
       
   553             'label' => 'above',
       
   554             'settings' => array(),
       
   555             'weight' => 0,
       
   556           );
       
   557         }
       
   558       }
       
   559     }
       
   560 
       
   561     return $instance;
       
   562   }
       
   563 
       
   564   /**
       
   565    * Prepares widget properties for the current run-time context.
       
   566    *
       
   567    * @param $widget
       
   568    *   Widget specifications as found in $instance['widget'].
       
   569    * @param $field_type
       
   570    *   The field type.
       
   571    *
       
   572    * @return
       
   573    *   The widget properties completed for the current runtime context.
       
   574    */
       
   575   public function prepareInstanceWidget($widget, $field_type) {
       
   576     $field_type_info = field_info_field_types($field_type);
       
   577 
       
   578     // Fill in default values.
       
   579     $widget += array(
       
   580       'type' => $field_type_info['default_widget'],
       
   581       'settings' => array(),
       
   582       'weight' => 0,
       
   583     );
       
   584 
       
   585     $widget_type_info = field_info_widget_types($widget['type']);
       
   586     // Fall back to default formatter if formatter type is not available.
       
   587     if (!$widget_type_info) {
       
   588       $widget['type'] = $field_type_info['default_widget'];
       
   589       $widget_type_info = field_info_widget_types($widget['type']);
       
   590     }
       
   591     $widget['module'] = $widget_type_info['module'];
       
   592     // Fill in default settings for the widget.
       
   593     $widget['settings'] += field_info_widget_settings($widget['type']);
       
   594 
       
   595     return $widget;
       
   596   }
       
   597 
       
   598   /**
       
   599    * Adapts display specifications to the current run-time context.
       
   600    *
       
   601    * @param $display
       
   602    *   Display specifications as found in $instance['display']['a_view_mode'].
       
   603    * @param $field_type
       
   604    *   The field type.
       
   605    *
       
   606    * @return
       
   607    *   The display properties completed for the current runtime context.
       
   608    */
       
   609   public function prepareInstanceDisplay($display, $field_type) {
       
   610     $field_type_info = field_info_field_types($field_type);
       
   611 
       
   612     // Fill in default values.
       
   613     $display += array(
       
   614       'label' => 'above',
       
   615       'settings' => array(),
       
   616       'weight' => 0,
       
   617     );
       
   618     if (empty($display['type'])) {
       
   619       $display['type'] = $field_type_info['default_formatter'];
       
   620     }
       
   621     if ($display['type'] != 'hidden') {
       
   622       $formatter_type_info = field_info_formatter_types($display['type']);
       
   623       // Fall back to default formatter if formatter type is not available.
       
   624       if (!$formatter_type_info) {
       
   625         $display['type'] = $field_type_info['default_formatter'];
       
   626         $formatter_type_info = field_info_formatter_types($display['type']);
       
   627       }
       
   628       $display['module'] = $formatter_type_info['module'];
       
   629       // Fill in default settings for the formatter.
       
   630       $display['settings'] += field_info_formatter_settings($display['type']);
       
   631     }
       
   632 
       
   633     return $display;
       
   634   }
       
   635 
       
   636   /**
       
   637    * Prepares 'extra fields' for the current run-time context.
       
   638    *
       
   639    * @param $extra_fields
       
   640    *   The array of extra fields, as collected in hook_field_extra_fields().
       
   641    * @param $entity_type
       
   642    *   The entity type.
       
   643    * @param $bundle
       
   644    *   The bundle name.
       
   645    *
       
   646    * @return
       
   647    *   The list of extra fields completed for the current runtime context.
       
   648    */
       
   649   public function prepareExtraFields($extra_fields, $entity_type, $bundle) {
       
   650     $entity_type_info = entity_get_info($entity_type);
       
   651     $bundle_settings = field_bundle_settings($entity_type, $bundle);
       
   652     $extra_fields += array('form' => array(), 'display' => array());
       
   653 
       
   654     $result = array();
       
   655     // Extra fields in forms.
       
   656     foreach ($extra_fields['form'] as $name => $field_data) {
       
   657       $settings = isset($bundle_settings['extra_fields']['form'][$name]) ? $bundle_settings['extra_fields']['form'][$name] : array();
       
   658       if (isset($settings['weight'])) {
       
   659         $field_data['weight'] = $settings['weight'];
       
   660       }
       
   661       $result['form'][$name] = $field_data;
       
   662     }
       
   663 
       
   664     // Extra fields in displayed entities.
       
   665     $data = $extra_fields['display'];
       
   666     foreach ($extra_fields['display'] as $name => $field_data) {
       
   667       $settings = isset($bundle_settings['extra_fields']['display'][$name]) ? $bundle_settings['extra_fields']['display'][$name] : array();
       
   668       $view_modes = array_merge(array('default'), array_keys($entity_type_info['view modes']));
       
   669       foreach ($view_modes as $view_mode) {
       
   670         if (isset($settings[$view_mode])) {
       
   671           $field_data['display'][$view_mode] = $settings[$view_mode];
       
   672         }
       
   673         else {
       
   674           $field_data['display'][$view_mode] = array(
       
   675             'weight' => $field_data['weight'],
       
   676             'visible' => TRUE,
       
   677           );
       
   678         }
       
   679       }
       
   680       unset($field_data['weight']);
       
   681       $result['display'][$name] = $field_data;
       
   682     }
       
   683 
       
   684     return $result;
       
   685   }
       
   686 }