diff --git a/modules/field/field.api.php b/modules/field/field.api.php index e37254a1631..bc4e266905c 100644 --- a/modules/field/field.api.php +++ b/modules/field/field.api.php @@ -149,6 +149,10 @@ function hook_field_extra_fields_alter(&$info) { * instance definition. This formatter must be available whenever the field * type is available (i.e. provided by the field type module, or by a module * the field type module depends on). + * - no_ui: (optional) A boolean specifying that users should not be allowed + * to create fields and instances of this field type through the UI. Such + * fields can only be created programmatically with field_create_field() + * and field_create_instance(). Defaults to FALSE. * * @see hook_field_info_alter() */ diff --git a/modules/field/tests/field_test.field.inc b/modules/field/tests/field_test.field.inc index faf1b146973..116e94a6aba 100644 --- a/modules/field/tests/field_test.field.inc +++ b/modules/field/tests/field_test.field.inc @@ -12,7 +12,7 @@ function field_test_field_info() { return array( 'test_field' => array( - 'label' => t('Test Field'), + 'label' => t('Test field'), 'description' => t('Dummy field type used for tests.'), 'settings' => array( 'test_field_setting' => 'dummy test string', @@ -26,6 +26,15 @@ function field_test_field_info() { 'default_widget' => 'test_field_widget', 'default_formatter' => 'field_test_default', ), + 'hidden_test_field' => array( + 'no_ui' => TRUE, + 'label' => t('Hidden from UI test field'), + 'description' => t('Dummy hidden field type used for tests.'), + 'settings' => array(), + 'instance_settings' => array(), + 'default_widget' => 'test_field_widget', + 'default_formatter' => 'field_test_default', + ), ); } @@ -139,7 +148,7 @@ function field_test_field_widget_info() { return array( 'test_field_widget' => array( 'label' => t('Test field'), - 'field types' => array('test_field'), + 'field types' => array('test_field', 'hidden_test_field'), 'settings' => array('test_widget_setting' => 'dummy test string'), ), 'test_field_widget_multiple' => array( diff --git a/modules/field_ui/field_ui.admin.inc b/modules/field_ui/field_ui.admin.inc index 5c8b45e3217..6be92ee591d 100644 --- a/modules/field_ui/field_ui.admin.inc +++ b/modules/field_ui/field_ui.admin.inc @@ -703,8 +703,9 @@ function field_ui_field_type_options() { $field_types = field_info_field_types(); $field_type_options = array(); foreach ($field_types as $name => $field_type) { - // Skip field types which have no widget types. - if (field_ui_widget_type_options($name)) { + // Skip field types which have no widget types, or should not be add via + // uesr interface. + if (field_ui_widget_type_options($name) && empty($field_type['no_ui'])) { $options[$name] = $field_type['label']; } } @@ -793,10 +794,13 @@ function field_ui_existing_field_options($entity_type, $bundle) { // Don't show // - locked fields, // - fields already in the current bundle, - // - field that cannot be added to the entity type. + // - fields that cannot be added to the entity type, + // - fields that that shoud not be added via user interface. + if (empty($field['locked']) && !field_info_instance($entity_type, $field['field_name'], $bundle) - && (empty($field['entity_types']) || in_array($entity_type, $field['entity_types']))) { + && (empty($field['entity_types']) || in_array($entity_type, $field['entity_types'])) + && empty($field_types[$field['type']]['no_ui'])) { $text = t('@type: @field (@label)', array( '@type' => $field_types[$field['type']]['label'], '@label' => t($instance['label']), '@field' => $instance['field_name'], diff --git a/modules/field_ui/field_ui.test b/modules/field_ui/field_ui.test index 171d4c4bc32..a45e0bcca49 100644 --- a/modules/field_ui/field_ui.test +++ b/modules/field_ui/field_ui.test @@ -125,7 +125,7 @@ class FieldUITestCase extends DrupalWebTestCase { */ function addExistingField() { // Check "Add existing field" appears. - $this->drupalGet(('admin/structure/types/manage/page/fields')); + $this->drupalGet('admin/structure/types/manage/page/fields'); $this->assertRaw(t('Add existing field'), t('"Add existing field" was found.')); // Check that the list of options respects entity type restrictions on @@ -263,6 +263,41 @@ class FieldUITestCase extends DrupalWebTestCase { $this->assertNull(field_info_field($this->field_name), t('Field was deleted.')); } + /** + * Test that Field UI respects the 'no_ui' option in hook_field_info(). + */ + function testHiddenFields() { + $bundle_path = 'admin/structure/types/manage/' . $this->hyphen_type . '/fields/'; + + // Check that the field type is not available in the 'add new field' row. + $this->drupalGet($bundle_path); + $this->assertFalse($this->xpath('//select[@id="edit--add-new-field-type"]//option[@value="hidden_test_field"]'), t("The 'add new field' select respects field types 'no_ui' property.")); + + // Create a field and an instance programmatically. + $field_name = 'hidden_test_field'; + field_create_field(array('field_name' => $field_name, 'type' => $field_name)); + $instance = array( + 'field_name' => $field_name, + 'bundle' => $this->type, + 'entity_type' => 'node', + 'label' => t('Hidden field'), + 'widget_type' => 'test_field_widget', + ); + field_create_instance($instance); + $this->assertTrue(field_read_instance('node', $field_name, $this->type), t('An instance of the field %field was created programmatically.', array('%field' => $field_name))); + + // Check that the newly added instance appears on the 'Manage Fields' + // screen. + $this->drupalGet($bundle_path); + $this->assertFieldByXPath('//table[@id="field-overview"]//span[@class="label-field"]', $instance['label'], t('Field was created and appears in the overview page.')); + + // Check that the instance does not appear in the 'add existing field' row + // on other bundles. + $bundle_path = 'admin/structure/types/manage/article/fields/'; + $this->drupalGet($bundle_path); + $this->assertFalse($this->xpath('//select[@id="edit--add-existing-field-field-name"]//option[@value=:field_name]', array(':field_name' => $field_name)), t("The 'add existing field' select respects field types 'no_ui' property.")); + } + /** * Create a new field through the Field UI. *