/[drupal]/drupal/modules/field/field.form.inc
ViewVC logotype

Contents of /drupal/modules/field/field.form.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.32 - (show annotations) (download) (as text)
Sun Nov 1 14:05:31 2009 UTC (3 weeks, 4 days ago) by dries
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10
Changes since 1.31: +31 -40 lines
File MIME type: text/x-php
- Patch #567064 by yched, sun: widgets done 'the easy way' have too many limitations. Removes more code than it adds!
1 <?php
2 // $Id: field.form.inc,v 1.31 2009/10/31 16:06:35 dries Exp $
3
4 /**
5 * @file
6 * Field forms management.
7 */
8
9 /**
10 * Create a separate form element for each field.
11 */
12 function field_default_form($obj_type, $object, $field, $instance, $langcode, $items, &$form, &$form_state, $get_delta = NULL) {
13 // This could be called with no object, as when a UI module creates a
14 // dummy form to set default values.
15 if ($object) {
16 list($id, , ) = entity_extract_ids($obj_type, $object);
17 }
18 $addition = array();
19
20 $field_name = $field['field_name'];
21
22 // If the field is not accessible, don't add anything. The field value will
23 // be left unchanged on update, or considered empty on insert (default value
24 // will be inserted if applicable).
25 if (!field_access('edit', $field, $obj_type, $object)) {
26 return $addition;
27 }
28
29 // Put field information at the top of the form, so that it can be easily
30 // retrieved.
31 // Note : widgets and other form handling code should *always* fetch
32 // field and instance information from $form['#fields'] rather than from
33 // field_info_field(). This lets us build forms for 'variants' of a field,
34 // for instance on admin screens.
35 $form['#fields'][$field_name] = array(
36 'field' => $field,
37 'instance' => $instance,
38 );
39
40 // Populate widgets with default values when creating a new object.
41 if (empty($items) && empty($id)) {
42 $items = field_get_default_value($obj_type, $object, $field, $instance, $langcode);
43 }
44
45 $form_element = array();
46
47 // If field module handles multiple values for this form element,
48 // and we are displaying an individual element, process the multiple value
49 // form.
50 if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
51 $form_element = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state);
52 }
53 // If the widget is handling multiple values (e.g Options),
54 // or if we are displaying an individual element, just get a single form
55 // element and make it the $delta value.
56 else {
57 $delta = isset($get_delta) ? $get_delta : 0;
58 $function = $instance['widget']['module'] . '_field_widget';
59 if (function_exists($function)) {
60 $element = array(
61 '#object_type' => $instance['object_type'],
62 '#bundle' => $instance['bundle'],
63 '#field_name' => $field['field_name'],
64 '#columns' => array_keys($field['columns']),
65 '#title' => check_plain(t($instance['label'])),
66 '#description' => field_filter_xss($instance['description']),
67 // Only the first widget should be required.
68 '#required' => $delta == 0 && $instance['required'],
69 '#delta' => $delta,
70 );
71 if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
72 // If we're processing a specific delta value for a field where the
73 // field module handles multiples, set the delta in the result.
74 // For fields that handle their own processing, we can't make assumptions
75 // about how the field is structured, just merge in the returned value.
76 if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) {
77 $form_element[$delta] = $element;
78 }
79 else {
80 $form_element = $element;
81 }
82 }
83 }
84 }
85
86 if ($form_element) {
87 // Add the field form element as a child keyed by language code to match the
88 // field data structure: $object->{$field_name}[$langcode][$delta][$column].
89 // The '#language' key can be used to access the field's form element when
90 // $langcode is unknown. The #weight property is inherited from the field's
91 // form element.
92 $addition[$field['field_name']] = array(
93 '#tree' => TRUE,
94 '#weight' => $instance['widget']['weight'],
95 '#language' => $langcode,
96 $langcode => $form_element,
97 );
98
99 $form['#fields'][$field['field_name']]['form_path'] = array($field['field_name']);
100 }
101
102 return $addition;
103 }
104
105 /**
106 * Special handling to create form elements for multiple values.
107 *
108 * Handles generic features for multiple fields:
109 * - number of widgets
110 * - AHAH-'add more' button
111 * - drag-n-drop value reordering
112 */
113 function field_multiple_value_form($field, $instance, $langcode, $items, &$form, &$form_state) {
114 $field_name = $field['field_name'];
115
116 switch ($field['cardinality']) {
117 case FIELD_CARDINALITY_UNLIMITED:
118 $filled_items = field_set_empty($field, $items);
119 $current_item_count = isset($form_state['field_item_count'][$field_name])
120 ? $form_state['field_item_count'][$field_name]
121 : count($items);
122 // We always want at least one empty icon for the user to fill in.
123 $max = ($current_item_count > count($filled_items))
124 ? $current_item_count - 1
125 : $current_item_count;
126
127 break;
128 default:
129 $max = $field['cardinality'] - 1;
130 break;
131 }
132
133 $title = check_plain(t($instance['label']));
134 $description = field_filter_xss(t($instance['description']));
135
136 $wrapper_id = str_replace('_', '-', $field_name) . '-wrapper';
137
138 $form_element = array(
139 '#theme' => 'field_multiple_value_form',
140 '#field_name' => $field['field_name'],
141 '#cardinality' => $field['cardinality'],
142 '#title' => $title,
143 '#required' => $instance['required'],
144 '#description' => $description,
145 '#prefix' => '<div id="' . $wrapper_id . '">',
146 '#suffix' => '</div>',
147 '#max_delta' => $max,
148 );
149
150 $function = $instance['widget']['module'] . '_field_widget';
151 if (function_exists($function)) {
152 for ($delta = 0; $delta <= $max; $delta++) {
153 $multiple = $field['cardinality'] > 1 || $field['cardinality'] == FIELD_CARDINALITY_UNLIMITED;
154 $element = array(
155 '#object_type' => $instance['object_type'],
156 '#bundle' => $instance['bundle'],
157 '#field_name' => $field_name,
158 '#columns' => array_keys($field['columns']),
159 // For multiple fields, title and description are handled by the wrapping table.
160 '#title' => $multiple ? '' : $title,
161 '#description' => $multiple ? '' : $description,
162 // Only the first widget should be required.
163 '#required' => $delta == 0 && $instance['required'],
164 '#delta' => $delta,
165 '#weight' => $delta,
166 );
167 if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) {
168 // Input field for the delta (drag-n-drop reordering).
169 if ($multiple) {
170 // We name the element '_weight' to avoid clashing with elements
171 // defined by widget.
172 $element['_weight'] = array(
173 '#type' => 'weight',
174 // Note: this 'delta' is the FAPI 'weight' element's property.
175 '#delta' => $max,
176 '#default_value' => isset($items[$delta]['_weight']) ? $items[$delta]['_weight'] : $delta,
177 '#weight' => 100,
178 );
179 }
180 $form_element[$delta] = $element;
181 }
182 }
183
184 // Add 'add more' button, if not working with a programmed form.
185 if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) {
186 $form_element[$field_name . '_add_more'] = array(
187 '#type' => 'submit',
188 '#name' => $field_name . '_add_more',
189 '#value' => t('Add another item'),
190 '#attributes' => array('class' => array('field-add-more-submit')),
191 // Submit callback for disabled JavaScript.
192 '#submit' => array('field_add_more_submit'),
193 '#ajax' => array(
194 'callback' => 'field_add_more_js',
195 'wrapper' => $wrapper_id,
196 'method' => 'replace',
197 'effect' => 'fade',
198 ),
199 // The field_add_more_submit() and field_add_more_js() handlers will
200 // find the relevant field using those entries.
201 '#field_name' => $field_name,
202 '#language' => $langcode,
203 );
204 }
205 }
206
207 return $form_element;
208 }
209
210 /**
211 * Theme an individual form element.
212 *
213 * Combine multiple values into a table with drag-n-drop reordering.
214 * TODO : convert to a template.
215 */
216 function theme_field_multiple_value_form($variables) {
217 $element = $variables['element'];
218 $output = '';
219
220 if ($element['#cardinality'] > 1 || $element['#cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
221 $table_id = $element['#field_name'] . '_values';
222 $order_class = $element['#field_name'] . '-delta-order';
223 $required = !empty($element['#required']) ? '<span class="form-required" title="' . t('This field is required. ') . '">*</span>' : '';
224
225 $header = array(
226 array(
227 'data' => '<label>' . t('!title: !required', array('!title' => $element['#title'], '!required' => $required)) . "</label>",
228 'colspan' => 2,
229 'class' => array('field-label'),
230 ),
231 t('Order'),
232 );
233 $rows = array();
234
235 // Sort items according to '_weight' (needed when the form comes back after
236 // preview or failed validation)
237 $items = array();
238 foreach (element_children($element) as $key) {
239 if ($key === $element['#field_name'] . '_add_more') {
240 $add_more_button = &$element[$key];
241 }
242 else {
243 $items[] = &$element[$key];
244 }
245 }
246 usort($items, '_field_sort_items_value_helper');
247
248 // Add the items as table rows.
249 foreach ($items as $key => $item) {
250 $item['_weight']['#attributes']['class'] = array($order_class);
251 $delta_element = drupal_render($item['_weight']);
252 $cells = array(
253 array('data' => '', 'class' => array('field-multiple-drag')),
254 drupal_render($item),
255 array('data' => $delta_element, 'class' => array('delta-order')),
256 );
257 $rows[] = array(
258 'data' => $cells,
259 'class' => array('draggable'),
260 );
261 }
262
263 $output = '<div class="form-item">';
264 $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id, 'class' => array('field-multiple-table'))));
265 $output .= $element['#description'] ? '<div class="description">' . $element['#description'] . '</div>' : '';
266 $output .= '<div class="clearfix">' . drupal_render($add_more_button) . '</div>';
267 $output .= '</div>';
268
269 drupal_add_tabledrag($table_id, 'order', 'sibling', $order_class);
270 }
271 else {
272 foreach (element_children($element) as $key) {
273 $output .= drupal_render($element[$key]);
274 }
275 }
276
277 return $output;
278 }
279
280
281 /**
282 * Transfer field-level validation errors to widgets.
283 */
284 function field_default_form_errors($obj_type, $object, $field, $instance, $langcode, $items, $form, $errors) {
285 $field_name = $field['field_name'];
286 if (!empty($errors[$field_name][$langcode])) {
287 $function = $instance['widget']['module'] . '_field_widget_error';
288 $function_exists = function_exists($function);
289
290 // Walk the form down to where the widget lives.
291 $form_path = $form['#fields'][$field_name]['form_path'];
292 $element = $form;
293 foreach ($form_path as $key) {
294 $element = $element[$key];
295 }
296
297 $multiple_widget = field_behaviors_widget('multiple values', $instance) != FIELD_BEHAVIOR_DEFAULT;
298 foreach ($errors[$field_name][$langcode] as $delta => $delta_errors) {
299 // For multiple single-value widgets, pass errors by delta.
300 // For a multiple-value widget, all errors are passed to the main widget.
301 $error_element = $multiple_widget ? $element[$langcode] : $element[$langcode][$delta];
302 foreach ($delta_errors as $error) {
303 if ($function_exists) {
304 $function($error_element, $error);
305 }
306 else {
307 // Make sure that errors are reported (even incorrectly flagged) if
308 // the widget module fails to implement hook_field_widget_error().
309 form_error($error_element, $error['error']);
310 }
311 }
312 }
313 }
314 }
315
316 /**
317 * Submit handler to add more choices to a field form. This handler is used when
318 * JavaScript is not available. It makes changes to the form state and the
319 * entire form is rebuilt during the page reload.
320 */
321 function field_add_more_submit($form, &$form_state) {
322 // Set the form to rebuild and run submit handlers.
323 if (isset($form['#builder_function']) && function_exists($form['#builder_function'])) {
324 $entity = $form['#builder_function']($form, $form_state);
325
326 // Make the changes we want to the form state.
327 $field_name = $form_state['clicked_button']['#field_name'];
328 $langcode = $form_state['clicked_button']['#language'];
329 if ($form_state['values'][$field_name . '_add_more']) {
330 $form_state['field_item_count'][$field_name] = count($form_state['values'][$field_name][$langcode]);
331 }
332 }
333 }
334
335 /**
336 * Ajax callback for addition of new empty widgets.
337 */
338 function field_add_more_js($form, $form_state) {
339 // Retrieve field information.
340 $field_name = $form_state['clicked_button']['#field_name'];
341 $field = $form['#fields'][$field_name]['field'];
342 if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) {
343 ajax_render(array());
344 }
345 // Navigate to the right part of the form.
346 $form_path = $form['#fields'][$field_name]['form_path'];
347 $field_form = $form;
348 foreach ($form_path as $key) {
349 $field_form = $field_form[$key];
350 }
351
352 // Add a DIV around the new field to receive the AJAX effect.
353 $langcode = $field_form['#language'];
354 $delta = $field_form[$langcode]['#max_delta'];
355 $field_form[$langcode][$delta]['#prefix'] = '<div class="ajax-new-content">' . (isset($field_form[$langcode][$delta]['#prefix']) ? $field_form[$langcode][$delta]['#prefix'] : '');
356 $field_form[$langcode][$delta]['#suffix'] = (isset($field_form[$langcode][$delta]['#suffix']) ? $field_form[$langcode][$delta]['#suffix'] : '') . '</div>';
357
358 return drupal_render($field_form);
359 }

  ViewVC Help
Powered by ViewVC 1.1.2