diff --git a/modules/field/field.form.inc b/modules/field/field.form.inc index 372b712b88b..8b893ed32f4 100644 --- a/modules/field/field.form.inc +++ b/modules/field/field.form.inc @@ -15,21 +15,13 @@ function field_default_form($obj_type, $object, $field, $instance, $langcode, $i if ($object) { list($id, , ) = entity_extract_ids($obj_type, $object); } - $addition = array(); $field_name = $field['field_name']; - // If the field is not accessible, don't add anything. The field value will - // be left unchanged on update, or considered empty on insert (default value - // will be inserted if applicable). - if (!field_access('edit', $field, $obj_type, $object)) { - return $addition; - } - // Put field information at the top of the form, so that it can be easily // retrieved. - // Note : widgets and other form handling code should *always* fetch - // field and instance information from $form['#fields'] rather than from + // Note : widgets and other form handling code should *always* fetch field + // and instance information from $form['#fields'] rather than from // field_info_field(). This lets us build forms for 'variants' of a field, // for instance on admin screens. $form['#fields'][$field_name] = array( @@ -42,54 +34,58 @@ function field_default_form($obj_type, $object, $field, $instance, $langcode, $i $items = field_get_default_value($obj_type, $object, $field, $instance, $langcode); } - $form_element = array(); + $field_elements = array(); - // If field module handles multiple values for this form element, - // and we are displaying an individual element, process the multiple value - // form. - if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { - $form_element = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state); - } - // If the widget is handling multiple values (e.g Options), - // or if we are displaying an individual element, just get a single form - // element and make it the $delta value. - else { - $delta = isset($get_delta) ? $get_delta : 0; - $function = $instance['widget']['module'] . '_field_widget'; - if (function_exists($function)) { - $element = array( - '#object_type' => $instance['object_type'], - '#bundle' => $instance['bundle'], - '#field_name' => $field_name, - '#columns' => array_keys($field['columns']), - '#title' => check_plain(t($instance['label'])), - '#description' => field_filter_xss($instance['description']), - // Only the first widget should be required. - '#required' => $delta == 0 && $instance['required'], - '#delta' => $delta, - ); - if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) { - // If we're processing a specific delta value for a field where the - // field module handles multiples, set the delta in the result. - // For fields that handle their own processing, we can't make assumptions - // about how the field is structured, just merge in the returned value. - if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { - $form_element[$delta] = $element; - } - else { - $form_element = $element; + if (field_access('edit', $field, $obj_type, $object)) { + // If field module handles multiple values for this form element, and we + // are displaying an individual element, process the multiple value form. + if (!isset($get_delta) && field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { + $field_elements = field_multiple_value_form($field, $instance, $langcode, $items, $form, $form_state); + } + // If the widget is handling multiple values (e.g Options), or if we are + // displaying an individual element, just get a single form element and + // make it the $delta value. + else { + $delta = isset($get_delta) ? $get_delta : 0; + $function = $instance['widget']['module'] . '_field_widget'; + if (function_exists($function)) { + $element = array( + '#object_type' => $instance['object_type'], + '#bundle' => $instance['bundle'], + '#field_name' => $field_name, + '#columns' => array_keys($field['columns']), + '#title' => check_plain(t($instance['label'])), + '#description' => field_filter_xss($instance['description']), + // Only the first widget should be required. + '#required' => $delta == 0 && $instance['required'], + '#delta' => $delta, + ); + if ($element = $function($form, $form_state, $field, $instance, $langcode, $items, $delta, $element)) { + // If we're processing a specific delta value for a field where the + // field module handles multiples, set the delta in the result. + // For fields that handle their own processing, we can't make + // assumptions about how the field is structured, just merge in the + // returned element. + if (field_behaviors_widget('multiple values', $instance) == FIELD_BEHAVIOR_DEFAULT) { + $field_elements[$delta] = $element; + } + else { + $field_elements = $element; + } } } } } - if ($form_element) { - // Add the field form element as a child keyed by language code to match the - // field data structure: $object->{$field_name}[$langcode][$delta][$column]. - // The '#language' key can be used to access the field's form element when - // $langcode is unknown. The #weight property is inherited from the field's - // form element. - // Also aid in theming of field widgets by rendering a classified container. + if ($field_elements) { + // Add the field form element as a child keyed by language code to match + // the field data structure: + // $object->{$field_name}[$langcode][$delta][$column]. + // The '#language' key can be used to access the field's form element + // when $langcode is unknown. The #weight property is inherited from the + // field's form element. + // Also aid in theming of field widgets by rendering a classified + // container. $addition[$field_name] = array( '#type' => 'container', '#attributes' => array( @@ -102,11 +98,26 @@ function field_default_form($obj_type, $object, $field, $instance, $langcode, $i '#tree' => TRUE, '#weight' => $instance['widget']['weight'], '#language' => $langcode, - $langcode => $form_element, + $langcode => $field_elements, ); - - $form['#fields'][$field_name]['form_path'] = array($field_name); } + else { + // The field is not accessible, or the widget did not return anything. Make + // sure the items are available in the submitted form values. + foreach ($items as $delta => $item) { + $field_elements[$delta] = array( + '#type' => 'value', + '#value' => $item, + ); + } + $addition[$field_name] = array( + '#tree' => TRUE, + '#language' => $langcode, + $langcode => $field_elements, + ); + } + + $form['#fields'][$field_name]['form_path'] = array($field_name); return $addition; } @@ -122,6 +133,7 @@ function field_default_form($obj_type, $object, $field, $instance, $langcode, $i function field_multiple_value_form($field, $instance, $langcode, $items, &$form, &$form_state) { $field_name = $field['field_name']; + // Determine the number of widgets to display. switch ($field['cardinality']) { case FIELD_CARDINALITY_UNLIMITED: $filled_items = field_set_empty($field, $items); @@ -141,20 +153,8 @@ function field_multiple_value_form($field, $instance, $langcode, $items, &$form, $title = check_plain(t($instance['label'])); $description = field_filter_xss(t($instance['description'])); - - $wrapper_id = str_replace('_', '-', $field_name) . '-wrapper'; - - $form_element = array( - '#theme' => 'field_multiple_value_form', - '#field_name' => $field['field_name'], - '#cardinality' => $field['cardinality'], - '#title' => $title, - '#required' => $instance['required'], - '#description' => $description, - '#prefix' => '
', - '#suffix' => '
', - '#max_delta' => $max, - ); + $wrapper_id = drupal_html_class($field_name) . '-wrapper'; + $field_elements = array(); $function = $instance['widget']['module'] . '_field_widget'; if (function_exists($function)) { @@ -186,34 +186,47 @@ function field_multiple_value_form($field, $instance, $langcode, $items, &$form, '#weight' => 100, ); } - $form_element[$delta] = $element; + $field_elements[$delta] = $element; } } - // Add 'add more' button, if not working with a programmed form. - if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) { - $form_element['add_more'] = array( - '#type' => 'submit', - '#name' => $field_name . '_add_more', - '#value' => t('Add another item'), - '#attributes' => array('class' => array('field-add-more-submit')), - // Submit callback for disabled JavaScript. - '#submit' => array('field_add_more_submit'), - '#ajax' => array( - 'callback' => 'field_add_more_js', - 'wrapper' => $wrapper_id, - 'method' => 'replace', - 'effect' => 'fade', - ), - // The field_add_more_submit() and field_add_more_js() handlers will - // find the relevant field using those entries. - '#field_name' => $field_name, - '#language' => $langcode, + if ($field_elements) { + $field_elements += array( + '#theme' => 'field_multiple_value_form', + '#field_name' => $field['field_name'], + '#cardinality' => $field['cardinality'], + '#title' => $title, + '#required' => $instance['required'], + '#description' => $description, + '#prefix' => '
', + '#suffix' => '
', + '#max_delta' => $max, ); + // Add 'add more' button, if not working with a programmed form. + if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED && empty($form_state['programmed'])) { + $field_elements['add_more'] = array( + '#type' => 'submit', + '#name' => $field_name . '_add_more', + '#value' => t('Add another item'), + '#attributes' => array('class' => array('field-add-more-submit')), + // Submit callback for disabled JavaScript. + '#submit' => array('field_add_more_submit'), + '#ajax' => array( + 'callback' => 'field_add_more_js', + 'wrapper' => $wrapper_id, + 'method' => 'replace', + 'effect' => 'fade', + ), + // The field_add_more_submit() and field_add_more_js() handlers will + // find the relevant field using those entries. + '#field_name' => $field_name, + '#language' => $langcode, + ); + } } } - return $form_element; + return $field_elements; } /** diff --git a/modules/field/field.test b/modules/field/field.test index b85c4c59487..49a1a2452f9 100644 --- a/modules/field/field.test +++ b/modules/field/field.test @@ -1592,6 +1592,65 @@ class FieldFormTestCase extends FieldTestCase { $this->assertNoField("$this->field_name[$langcode][" . ($delta + 1) . '][value]', 'No extraneous widget is displayed'); } + /** + * Tests fields with no 'edit' access. + */ + function testFieldFormAccess() { + // Create a "regular" field. + $field = $this->field_single; + $field_name = $field['field_name']; + $instance = $this->instance; + $instance['field_name'] = $field_name; + field_create_field($field); + field_create_instance($instance); + + // Create a field with no edit access - see field_test_field_access(). + $field_no_access = array( + 'field_name' => 'field_no_edit_access', + 'type' => 'test_field', + ); + $field_name_no_access = $field_no_access['field_name']; + $instance_no_access = array( + 'field_name' => $field_name_no_access, + 'object_type' => 'test_entity', + 'bundle' => 'test_bundle', + 'default_value' => array(0 => array('value' => 99)), + ); + field_create_field($field_no_access); + field_create_instance($instance_no_access); + + $langcode = FIELD_LANGUAGE_NONE; + + // Display creation form. + $this->drupalGet('test-entity/add/test-bundle'); + $this->assertNoFieldByName("{$field_name_no_access}[$langcode][0][value]", '', t('Widget is not displayed if field access is denied.')); + + // Create entity. + $edit = array("{$field_name}[$langcode][0][value]" => 1); + $this->drupalPost(NULL, $edit, t('Save')); + preg_match('|test-entity/(\d+)/edit|', $this->url, $match); + $id = $match[1]; + + // Check that the default value was saved. + $entity = field_test_entity_test_load($id); + $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('Default value was saved for the field with no edit access.')); + $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 1, t('Entered value vas saved for the field with edit access.')); + + // Create a new revision. + $edit = array("{$field_name}[$langcode][0][value]" => 2, 'revision' => TRUE); + $this->drupalPost('test-entity/' . $id . '/edit', $edit, t('Save')); + + // Check that the new revision has the expected values. + $entity = field_test_entity_test_load($id); + $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.')); + $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.')); + + // Check that the revision is also saved in the revisions table. + $entity = field_test_entity_test_load($id, $entity->ftvid); + $this->assertEqual($entity->{$field_name_no_access}[$langcode][0]['value'], 99, t('New revision has the expected value for the field with no edit access.')); + $this->assertEqual($entity->{$field_name}[$langcode][0]['value'], 2, t('New revision has the expected value for the field with edit access.')); + } + /** * Execute a POST request on a AHAH callback. * diff --git a/modules/simpletest/tests/field_test.module b/modules/simpletest/tests/field_test.module index 538071deecd..a3cd45125b0 100644 --- a/modules/simpletest/tests/field_test.module +++ b/modules/simpletest/tests/field_test.module @@ -49,6 +49,15 @@ function field_test_menu() { return $items; } +/** + * Implements hook_field_access(). + */ +function field_test_field_access($op, $field, $obj_type, $object, $account) { + if ($field['field_name'] == "field_no_{$op}_access") { + return FALSE; + } + return TRUE; +} /** * @@ -312,7 +321,7 @@ function field_test_entity_add($fttype) { $fttype = str_replace('-', '_', $fttype); $entity = (object)array('fttype' => $fttype); drupal_set_title(t('Create test_entity @bundle', array('@bundle' => $fttype)), PASS_THROUGH); - return drupal_get_form('field_test_entity_form', $entity); + return drupal_get_form('field_test_entity_form', $entity, TRUE); } function field_test_entity_edit($entity) { @@ -323,7 +332,7 @@ function field_test_entity_edit($entity) { /** * Form to set the value of fields attached to our entity. */ -function field_test_entity_form($form, &$form_state, $entity) { +function field_test_entity_form($form, &$form_state, $entity, $add = FALSE) { if (isset($form_state['test_entity'])) { $entity = $form_state['test_entity'] + (array)$entity; } @@ -340,13 +349,15 @@ function field_test_entity_form($form, &$form_state, $entity) { $form['#builder_function'] = 'field_test_entity_form_submit_builder'; field_attach_form('test_entity', $entity, $form, $form_state); - $form['revision'] = array( - '#access' => user_access('administer field_test content'), - '#type' => 'checkbox', - '#title' => t('Create new revision'), - '#default_value' => FALSE, - '#weight' => 100, - ); + if (!$add) { + $form['revision'] = array( + '#access' => user_access('administer field_test content'), + '#type' => 'checkbox', + '#title' => t('Create new revision'), + '#default_value' => FALSE, + '#weight' => 100, + ); + } $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), @@ -391,6 +402,7 @@ function field_test_entity_form_submit($form, &$form_state) { */ function field_test_entity_form_submit_builder($form, &$form_state) { $entity = field_test_create_stub_entity($form_state['values']['ftid'], $form_state['values']['ftvid'], $form_state['values']['fttype']); + $entity->revision = !empty($form_state['values']['revision']); field_attach_submit('test_entity', $entity, $form, $form_state); $form_state['test_entity'] = (array)$entity;