summaryrefslogtreecommitdiff
path: root/modules/field/field.default.inc
blob: 30e843401ec8f769f09689436dd455beb127192c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
<?php
// $Id$

/**
 * @file
 * Default 'implementations' of hook_field_*().
 *
 * Handles common field housekeeping.
 * Those implementations are special, as field.module does not define any field
 * types. Those functions take care of default stuff common to all field types.
 */

function field_default_validate($obj_type, $object, $field, $instance, $items, $form) {
  // TODO: here we could validate that required fields are filled in (for programmatic save)
}

function field_default_submit($obj_type, &$object, $field, $instance, &$items, $form, &$form_state) {
  // Get field values from the submitted form values. Assigning them to $items
  // populates $object->field_name when we return from _field_invoke_default().

  // TODO D7: Allow the values to be form_altered to another location, like we
  // do for the form definition ($form['#fields'][$field_name]['form_path']) ?

  if (isset($form_state['values'][$field['field_name']])) {
    $items = $form_state['values'][$field['field_name']];

    // Remove the 'value' of the 'add more' button.
    unset($items[$field['field_name'] . '_add_more']);

    // TODO: the above should be moved to validate time (and values saved back
    // using form_set_value() ), so that hook_field_validate() works on clean data.
    // Not sure we'll want what's below in validate too.

    // Reorder items to account for drag-n-drop reordering.
    if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
      $items = _field_sort_items($field, $items);
    }

    // Filter out empty values.
    $items = field_set_empty($field, $items);

    // _field_invoke() does not add back items for fields not present in the
    // original $object, so add them manually.
    $object->{$field['field_name']} = $items;
  }
  else {
    // The form did not include this field, for instance because of access
    // rules: make sure any existing value for the field stays unchanged.
    unset($object->{$field['field_name']});
  }
}

/**
 * The 'view' operation constructs the $object in a way that you can use
 * drupal_render() to display the formatted output for an individual field.
 * i.e. print drupal_render($object->content['field_foo']);
 *
 * The code supports both single value formatters, which theme an individual
 * item value, and multiple value formatters, which theme all values for the
 * field in a single theme. The multiple value formatters could be used, for
 * instance, to plot field values on a single map or display them in a graph.
 * Single value formatters are the default, multiple value formatters can be
 * designated as such in formatter_info().
 *
 * The $object array will look like:
 *   $object->content['field_foo']['wrapper'] = array(
 *     '#theme' => 'field',
 *     '#title' => 'label'
 *     '#field_name' => 'field_name',
 *     '#object' => $object,
 *     '#object_type' => $obj_type,
 *     // Value of the $teaser param of hook_nodeapi('view').
 *     '#teaser' => $teaser,
 *     'items' =>
 *       0 => array(
 *         '#item' => $items[0],
 *         // Only for 'single-value' formatters
 *         '#theme' => $theme,
 *         '#field_name' => 'field_name',
 *         '#bundle' => $bundle,
 *         '#formatter' => $formatter_name,
 *         '#settings' => $formatter_settings,
 *         '#object' => $object,
 *         '#object_type' => $obj_type,
 *         '#delta' => 0,
 *       ),
 *       1 => array(
 *         '#item' => $items[1],
 *         // Only for 'single-value' formatters
 *         '#theme' => $theme,
 *         '#field_name' => 'field_name',
 *         '#bundle' => $bundle_name,
 *         '#formatter' => $formatter_name,
 *         '#settings' => $formatter_settings,
 *         '#object' => $object,
 *         '#object_type' => $obj_type,
 *         '#delta' => 1,
 *       ),
 *       // Only for 'multiple-value' formatters
 *       '#theme' => $theme,
 *       '#field_name' => 'field_name',
 *       '#bundle' => $bundle_name,
 *       '#formatter' => $formatter_name,
 *       '#settings' => $formatter_settings,
 *     ),
 *   );
 */
function field_default_view($obj_type, $object, $field, $instance, $items, $teaser) {
  list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);

  $addition = array();

  // Entities without build modes should provide a 'full' context.
  // NODE_BUILD_NORMAL is 0, and ('whatever' == 0) is TRUE, so we need a ===.
  if (!isset($object->build_mode)) {
    $context = 'full';
  }
  elseif ($object->build_mode === NODE_BUILD_NORMAL
    || $object->build_mode == NODE_BUILD_PREVIEW) {
    $context = $teaser ? 'teaser' : 'full';
  }
  else {
    $context = $object->build_mode;
  }

  // If we don't have specific settings for the current build_mode, we use the
  // (required) 'full' build_mode.
  $display = isset($instance['display'][$context]) ? $instance['display'][$context] : $instance['display']['full'];
  // Ensure we have a valid formatter and formatter settings.
  $display = _field_get_formatter($display, $field);

  if ($display['type'] && $display['type'] !== 'hidden') {
    $theme = 'field_formatter_' . $display['type'];
    $single = (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT);

    $label_display = $display['label'];
    if (isset($object->build_mode) && $object->build_mode == NODE_BUILD_SEARCH_INDEX) {
      $label_display = 'hidden';
    }

    $info = array(
      '#field_name' => $field['field_name'],
      '#bundle' => $bundle,
      '#object' => $object,
      '#object_type' => $obj_type,
    );

    $element = $info + array(
      '#theme' => 'field',
      '#title' => check_plain(t($instance['label'])),
      '#access' => field_access('view', $field),
      '#label_display' => $label_display,
      '#teaser' => $teaser,
      '#single' => $single,
      'items' => array(),
    );

    // Fill-in items.
    foreach ($items as $delta => $item) {
      $element['items'][$delta] = array(
        '#item' => $item,
        '#weight' => $delta,
      );
    }

    // Append formatter information either on each item ('single-value' formatter)
    // or at the upper 'items' level ('multiple-value' formatter)
    $format_info = $info + array(
      '#formatter' => $display['type'],
      '#settings' => $display['settings'],
      '#theme' => $theme,
    );

    if ($single) {
      foreach ($items as $delta => $item) {
        $element['items'][$delta] += $format_info;
        $element['items'][$delta]['#item']['#delta'] = $delta;
      }
    }
    else {
      $element['items'] += $format_info;
    }

    // The wrapper lets us get the themed output for the whole field
    // to populate the $FIELD_NAME_rendered variable for templates,
    // and hide it from the $content variable if needed.
    // See 'preprocess' op and theme_content_field_wrapper().
    $wrapper = $info + array(
      'field' => $element,
      '#weight' => $instance['weight'],
      '#post_render' => array('field_wrapper_post_render'),
      '#context' => $context,
    );

    $addition = array($field['field_name'] => $wrapper);
  }
  return $addition;
}

/**
 * Hide excluded fields from the $content variable in templates.
 */
function field_wrapper_post_render($content, $element) {
  $instance = field_info_instance($element['#field_name'], $element['#bundle']);
  if (theme('field_exclude', $content, $instance, $element['#context'])) {
    return '';
  }
  return $content;
}

/**
 * 'Theme' function for a field's addition to the combined template output,
 * i.e. the node's $content or the user's $user_profile value.
 * This allows more flexibility in templates : you can use custom markup
 * around a few specific fields, and print the rest normally.
 *
 * This is a theme function, so it can be overridden in different
 * themes to produce different results.
 *
 * The html for individual fields are available in the $FIELD_NAME_rendered
 * variables.
 *
 * @return
 *   Whether or not the field's content is to be added in this context.
 *   Uses the 'exclude' value from the field's display settings.
 */
function theme_field_exclude($content, $object, $context) {
  if (empty($object['display'])
    || empty($object['display'][$context])
    || empty($object['display'][$context]['exclude'])) {
    return FALSE;
  }
  else {
    return TRUE;
  }
}

function field_default_preprocess($obj_type, $object, $field, $instance, &$items) {
  return array(
    $field['field_name'] . '_rendered' => isset($object->content[$field['field_name']]['#children']) ? $object->content[$field['field_name']]['#children'] : '',
  );
}

function field_default_prepare_translation($obj_type, $object, $field, $instance, &$items) {
  $addition = array();
  if (isset($object->translation_source->$field['field_name'])) {
    $addition[$field['field_name']] = $object->translation_source->$field['field_name'];
  }
  return $addition;
}