diff --git a/entityreference.info b/entityreference.info index a5f964d..18c87de 100644 --- a/entityreference.info +++ b/entityreference.info @@ -26,5 +26,6 @@ files[] = views/entityreference_plugin_row_fields.inc files[] = tests/entityreference.handlers.test files[] = tests/entityreference.taxonomy.test files[] = tests/entityreference.admin.test +files[] = tests/entityreference.form.test files[] = tests/entityreference.feeds.test files[] = tests/entityreference.entity_translation.test diff --git a/plugins/selection/EntityReference_SelectionHandler_Views.class.php b/plugins/selection/EntityReference_SelectionHandler_Views.class.php index cbed33b..a318be9 100644 --- a/plugins/selection/EntityReference_SelectionHandler_Views.class.php +++ b/plugins/selection/EntityReference_SelectionHandler_Views.class.php @@ -170,7 +170,38 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio * Implements EntityReferenceHandler::validateAutocompleteInput(). */ public function validateAutocompleteInput($input, &$element, &$form_state, $form) { - return NULL; + $bundled_entities = $this->getReferencableEntities($input, '=', 6); + $entities = array(); + foreach ($bundled_entities as $entities_list) { + $entities += $entities_list; + } + if (empty($entities)) { + // Error if there are no entities available for a required field. + form_error($element, t('No items found for %label', array('%label' => $element['#title']))); + } + elseif (count($entities) > 5) { + // Error if there are more than 5 matching entities. + form_error($element, t('Too many items found for %label. Specify the one you want by appending the id in parentheses, like "@value (@id)"', array( + '%label' => $element['#title'], + '@value' => $input, + '@id' => key($entities), + ))); + } + elseif (count($entities) > 1) { + // More helpful error if there are only a few matching entities. + $multiples = array(); + foreach ($entities as $id => $name) { + $multiples[] = filter_xss($name, array()) . ' (' . (int) $id . ')'; + } + form_error($element, t('Multiple items found for %label: !multiple', array( + '%label' => $element['#title'], + '!multiple' => theme('item_list', array('items' => $multiples)), + ))); + } + else { + // Take the one and only matching entity. + return (int) key($entities); + } } /** diff --git a/tests/entityreference.form.test b/tests/entityreference.form.test new file mode 100644 index 0000000..efbacef --- /dev/null +++ b/tests/entityreference.form.test @@ -0,0 +1,274 @@ + 'Entity Reference Form', + 'description' => 'Tests Entity Reference form widgets.', + 'group' => 'Entity Reference', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + // Enable entityreference module after views module is enabled to load + // the selection plugin EntityReference_SelectionHandler_Views. + parent::setUp(array('entity', 'ctools', 'views')); + module_enable(array('entityreference')); + + // Create test user. + $this->admin_user = $this->drupalCreateUser(array('bypass node access')); + $this->drupalLogin($this->admin_user); + } + + /** + * Test that the Generic autocomplete widget validates the value properly. + */ + public function testAutocompleteValidationWithGenericSelectionHandler() { + $type_referenced = $this->drupalCreateContentType(); + $type_referencing = $this->drupalCreateContentType(); + + $field_name = 'field_' . $type_referenced->type; + $field = array( + 'field_name' => $field_name, + 'settings' => array( + 'handler' => 'base', + 'target_type' => 'node', + 'handler_settings' => array( + 'target_bundles' => array($type_referenced->type), + ), + ), + ); + $field_instance = array( + 'field_name' => $field_name, + 'bundle' => $type_referencing->type, + ); + $this->createEntityReferenceFieldForNode($field, $field_instance); + + $node = $this->drupalCreateNode(array('type' => $type_referenced->type)); + $title_valid = $node->title; + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_valid); + $this->assertNoText('There are no entities matching "' . $title_valid . '"', + 'No validation error occurs for a valid title.'); + + $title_invalid = $this->randomName(); + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_invalid); + $this->assertText('There are no entities matching "' . $title_invalid . '"', + 'A validation error occurs for an invalid title.'); + + $title_many_nodes_has = $this->randomName(); + for ($i = 0; $i < 6; $i++) { + $node = $this->drupalCreateNode(array( + 'type' => $type_referenced->type, + 'title' => $title_many_nodes_has, + )); + } + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_many_nodes_has); + $this->assertText('Many entities are called ' . $title_many_nodes_has . '.', + 'A validation error occurs for a title shared by too many nodes.'); + + $title_several_nodes_has = $this->randomName(); + for ($i = 0; $i < 2; $i++) { + $node = $this->drupalCreateNode(array( + 'type' => $type_referenced->type, + 'title' => $title_several_nodes_has, + )); + } + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_several_nodes_has); + $this->assertText('Multiple entities match this reference; ', + 'A validation error occurs for a title shared by several nodes.'); + } + + /** + * Test that the Views autocomplete widget validates the value properly. + */ + public function testAutocompleteValidationWithViewsSelectionHandler() { + list($view_name, $display_name) = $this->createNodeEntityReferenceView(); + + $type_referenced = $this->drupalCreateContentType(); + $type_referencing = $this->drupalCreateContentType(); + + $field_name = 'field_' . $type_referenced->type; + $field = array( + 'field_name' => $field_name, + 'settings' => array( + "handler" => "views", + "target_type" => "node", + "handler_settings" => array( + "view" => array( + "view_name" => $view_name, + "display_name" => $display_name, + "args" => array(), + ), + "behaviors" => array( + "views-select-list" => array( + "status" => 0, + ), + ), + ), + ), + ); + $field_instance = array( + 'field_name' => $field_name, + 'bundle' => $type_referencing->type, + ); + $this->createEntityReferenceFieldForNode($field, $field_instance); + + $node = $this->drupalCreateNode(array('type' => $type_referenced->type)); + $title_valid = $node->title; + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_valid); + $this->assertNoText('No items found for ' . $field_name, + 'No validation error occurs for a valid title.'); + + $title_invalid = $this->randomName(); + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_invalid); + $this->assertText('No items found for ' . $field_name, + 'A validation error occurs for an invalid title.'); + + $title_many_nodes_has = $this->randomName(); + for ($i = 0; $i < 6; $i++) { + $node = $this->drupalCreateNode(array( + 'type' => $type_referenced->type, + 'title' => $title_many_nodes_has, + )); + } + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_many_nodes_has); + $this->assertText('Too many items found for ' . $field_name . '.', + 'A validation error occurs for a title shared by too many nodes.'); + + $title_several_nodes_has = $this->randomName(); + for ($i = 0; $i < 2; $i++) { + $node = $this->drupalCreateNode(array( + 'type' => $type_referenced->type, + 'title' => $title_several_nodes_has, + )); + } + $this->postNodeFormWithEntityReference($type_referencing, $field_name, $title_several_nodes_has); + $this->assertText('Multiple items found for ' . $field_name . ':', + 'A validation error occurs for a title shared by several nodes.'); + } + + /** + * Helper method to create a base field and field instance. + * + * @param array $field + * The field to be created. + * @param array $field_instance + * The field instance to be created. + */ + protected function createEntityReferenceFieldForNode($field, $field_instance) { + + // Add the common settings. + $field += array( + 'type' => 'entityreference', + 'translatable' => FALSE, + 'entity_types' => array('node'), + ); + $field_instance += array( + 'entity_type' => 'node', + 'widget' => array( + 'type' => 'entityreference_autocomplete', + 'module' => 'entityreference', + ), + ); + + $field = field_create_field($field); + field_create_instance($field_instance); + } + + /** + * Hepler method to submit node creation form. + * + * @param object $type + * Node type object. + * @param string $field_name + * Name of the Entity Reference field. + * @param string $title + * The node title to enter into the Entity Reference field. + */ + protected function postNodeFormWithEntityReference($type, $field_name, $title) { + $type_path = 'node/add/' . str_replace('_', '-', $type->type); + $edit = array( + $field_name . '[und][0][target_id]' => $title, + ); + + $this->drupalPost($type_path, $edit, t('Save')); + } + + /** + * Create an Entity Reference Views view for node list. + * + * @return view + * A Views view with a node Entity Reference display. + */ + protected function createNodeEntityReferenceView() { + $view_name = 'test_entityreference_select_node'; + $display_name = 'entityreference_1'; + + $view = new view(); + $view->name = $view_name; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'node'; + $view->human_name = $view_name; + $view->core = 7; + $view->api_version = '3.0'; + $view->disabled = FALSE; + + /* Display: Master */ + $handler = $view->new_display('default', 'Master', 'default'); + $handler->display->display_options['use_more_always'] = FALSE; + $handler->display->display_options['access']['type'] = 'perm'; + $handler->display->display_options['cache']['type'] = 'none'; + $handler->display->display_options['query']['type'] = 'views_query'; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'full'; + $handler->display->display_options['style_plugin'] = 'default'; + $handler->display->display_options['row_plugin'] = 'fields'; + /* Field: Content: Title */ + $handler->display->display_options['fields']['title']['id'] = 'title'; + $handler->display->display_options['fields']['title']['table'] = 'node'; + $handler->display->display_options['fields']['title']['field'] = 'title'; + $handler->display->display_options['fields']['title']['label'] = ''; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE; + /* Filter criterion: Content: Published */ + $handler->display->display_options['filters']['status']['id'] = 'status'; + $handler->display->display_options['filters']['status']['table'] = 'node'; + $handler->display->display_options['filters']['status']['field'] = 'status'; + $handler->display->display_options['filters']['status']['value'] = 1; + $handler->display->display_options['filters']['status']['group'] = 1; + $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE; + + /* Display: Entity Reference */ + $handler = $view->new_display('entityreference', 'Entity Reference', $display_name); + $handler->display->display_options['defaults']['title'] = FALSE; + $handler->display->display_options['pager']['type'] = 'some'; + $handler->display->display_options['defaults']['style_plugin'] = FALSE; + $handler->display->display_options['style_plugin'] = 'entityreference_style'; + $handler->display->display_options['style_options']['search_fields'] = array('title' => 'title'); + $handler->display->display_options['defaults']['style_options'] = FALSE; + $handler->display->display_options['defaults']['row_plugin'] = FALSE; + $handler->display->display_options['row_plugin'] = 'entityreference_fields'; + $handler->display->display_options['defaults']['row_options'] = FALSE; + + $view->save(); + + return array($view_name, $display_name); + } + +}