Commit 62c93ff8 authored by webchick's avatar webchick

Issue #1253820 by yched, zserno, tim.plunkett, xjm, effulgentsia, plach: Fixed...

Issue #1253820 by yched, zserno, tim.plunkett, xjm, effulgentsia, plach: Fixed It's impossible to submit no value for a field that has a default value.
parent dc8fcbaf
......@@ -55,6 +55,23 @@ function hook_entity_info_alter(&$entity_info) {
$entity_info['node']['controller_class'] = 'Drupal\mymodule\MyCustomNodeStorageController';
}
/**
* Act on a newly created entity.
*
* This hook runs after a new entity object has just been instantiated. It can
* be used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*/
function hook_entity_create(\Drupal\Core\Entity\EntityInterface $entity) {
// @todo Remove the check for EntityNG once all entity types have been
// converted to it.
if (!isset($entity->foo) && ($entity instanceof \Drupal\Core\Entity\EntityNG)) {
$entity->foo->value = 'some_initial_value';
}
}
/**
* Act on entities when loaded.
*
......
......@@ -451,6 +451,10 @@ public function create(array $values) {
$entity->{$this->uuidKey} = $uuid->generate();
}
// Modules might need to add or change the data initially held by the new
// entity object, for instance to fill-in default values.
$this->invokeHook('create', $entity);
return $entity;
}
......
......@@ -263,7 +263,12 @@ public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User
public function language() {
// @todo: Replace by EntityNG implementation once all entity types have been
// converted to use the entity field API.
return !empty($this->langcode) ? language_load($this->langcode) : new Language(array('langcode' => LANGUAGE_NOT_SPECIFIED));
$language = language_load($this->langcode);
if (!$language) {
// Make sure we return a proper language object.
$language = new Language(array('langcode' => LANGUAGE_NOT_SPECIFIED));
}
return $language;
}
/**
......
......@@ -266,8 +266,7 @@ public function language() {
$language = $this->get('langcode')->language;
if (!$language) {
// Make sure we return a proper language object.
// @todo Refactor this, see: http://drupal.org/node/1834542.
$language = language_default();
$language = new Language(array('langcode' => LANGUAGE_NOT_SPECIFIED));
}
return $language;
}
......
......@@ -48,6 +48,21 @@ function hook_comment_update(Drupal\comment\Comment $comment) {
search_touch_node($comment->nid->value);
}
/**
* Act on a newly created comment.
*
* This hook runs after a new comment object has just been instantiated. It can
* be used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\comment\Plugin\Core\Entity\Comment $comment
* The comment object.
*/
function hook_comment_create(\Drupal\comment\Plugin\Core\Entity\Comment $comment) {
if (!isset($comment->foo)) {
$comment->foo = 'some_initial_value';
}
}
/**
* Act on comments being loaded from the database.
*
......
......@@ -47,7 +47,7 @@ function testApprovalAdminInterface() {
// Get unapproved comment id.
$this->drupalLogin($this->admin_user);
$anonymous_comment4 = $this->getUnapprovedComment($subject);
$anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->nid));
$anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'node_type' => '', 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->nid));
$this->drupalLogout();
$this->assertFalse($this->commentExists($anonymous_comment4), 'Anonymous comment was not published.');
......@@ -111,7 +111,7 @@ function testApprovalNodeInterface() {
// Get unapproved comment id.
$this->drupalLogin($this->admin_user);
$anonymous_comment4 = $this->getUnapprovedComment($subject);
$anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->nid));
$anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'node_type' => '', 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->nid));
$this->drupalLogout();
$this->assertFalse($this->commentExists($anonymous_comment4), 'Anonymous comment was not published.');
......
......@@ -50,6 +50,7 @@ function testCommentClasses() {
// Add a comment.
$comment = entity_create('comment', array(
'nid' => $node->nid,
'node_type' => 'node_type_' . $node->bundle(),
'uid' => $case['comment_uid'],
'status' => $case['comment_status'],
'subject' => $this->randomName(),
......
......@@ -48,6 +48,7 @@ function setUp() {
'nid' => $this->node_user_commented->nid,
'cid' => '',
'pid' => '',
'node_type' => '',
);
$this->comment = entity_create('comment', $comment);
$this->comment->save();
......
......@@ -1156,7 +1156,6 @@ function field_attach_presave($entity) {
* it leaves unspecified.
*/
function field_attach_insert(EntityInterface $entity) {
_field_invoke_default('insert', $entity);
_field_invoke('insert', $entity);
// Let any module insert field data before the storage engine, accumulating
......
......@@ -53,37 +53,6 @@ function field_default_validate(EntityInterface $entity, $field, $instance, $lan
}
}
/**
* Inserts a default value if no $entity->$field_name entry was provided.
*
* This can happen with programmatic saves, or on form-based creation where
* the current user doesn't have 'edit' permission for the field. This is the
* default field 'insert' operation.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for the operation.
* @param $field
* The field structure for the operation.
* @param $instance
* The instance structure for $field in $entity's bundle.
* @param $langcode
* The language associated with $items.
* @param $items
* An array that this function will populate with default values.
*/
function field_default_insert(EntityInterface $entity, $field, $instance, $langcode, &$items) {
// _field_invoke() populates $items with an empty array if the $entity has no
// entry for the field, so we check on the $entity itself.
// We also check that the current field translation is actually defined before
// assigning it a default value. This way we ensure that only the intended
// languages get a default value. Otherwise we could have default values for
// not yet open languages.
if (empty($entity) || (!isset($entity->{$field['field_name']}[$langcode]) && !property_exists($entity, $field['field_name'])) ||
(isset($entity->{$field['field_name']}[$langcode]) && count($entity->{$field['field_name']}[$langcode]) == 0)) {
$items = field_get_default_value($entity, $field, $instance, $langcode);
}
}
/**
* Copies source field values into the entity to be prepared.
*
......
......@@ -386,6 +386,43 @@ function field_data_type_info() {
return $items;
}
/**
* Implements hook_entity_create().
*/
function field_entity_create(EntityInterface $entity) {
$info = $entity->entityInfo();
if (!empty($info['fieldable'])) {
foreach ($entity->getTranslationLanguages() as $langcode => $language) {
field_populate_default_values($entity, $langcode);
}
}
}
/**
* Inserts a default value for each entity field not having one.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for the operation.
* @param string $langcode
* (optional) The field language code to fill-in with the default value.
* Defaults to the entity language.
*/
function field_populate_default_values(EntityInterface $entity, $langcode = NULL) {
$entity_type = $entity->entityType();
$langcode = $langcode ?: $entity->language()->langcode;
foreach (field_info_instances($entity_type, $entity->bundle()) as $field_name => $instance) {
$field = field_info_field($field_name);
$field_langcode = field_is_translatable($entity_type, $field) ? $langcode : LANGUAGE_NOT_SPECIFIED;
// We need to preserve existing values.
if (empty($entity->{$field_name}) || !array_key_exists($field_langcode, $entity->{$field_name})) {
$items = field_get_default_value($entity, $field, $instance, $field_langcode);
if (!empty($items)) {
$entity->{$field_name}[$field_langcode] = $items;
}
}
}
}
/**
* Implements hook_entity_field_info() to define all configured fields.
*/
......
......@@ -84,11 +84,6 @@ public function form(EntityInterface $entity, $langcode, array $items, array &$f
$field_name => array(),
);
// Populate widgets with default values when creating a new entity.
if (empty($items) && ($entity->isNew())) {
$items = field_get_default_value($entity, $field, $instance, $langcode);
}
// Store field information in $form_state.
if (!field_form_get_state($parents, $field_name, $langcode, $form_state)) {
$field_state = array(
......
......@@ -340,9 +340,12 @@ function testFieldAttachSaveMissingDataDefaultValue() {
$this->instance['default_value_function'] = 'field_test_default_value';
field_update_instance($this->instance);
// Verify that fields are populated with default values.
$entity_type = 'test_entity';
$entity_init = field_test_create_entity();
$langcode = LANGUAGE_NOT_SPECIFIED;
$default = field_test_default_value($entity_init, $this->field, $this->instance);
$this->assertEqual($entity_init->{$this->field_name}[$langcode], $default, 'Default field value correctly populated.');
// Insert: Field is NULL.
$entity = clone($entity_init);
......@@ -350,6 +353,7 @@ function testFieldAttachSaveMissingDataDefaultValue() {
field_attach_insert($entity);
$entity = clone($entity_init);
$entity->{$this->field_name} = array();
field_attach_load($entity_type, array($entity->ftid => $entity));
$this->assertTrue(empty($entity->{$this->field_name}[$langcode]), 'Insert: NULL field results in no value saved');
......@@ -359,9 +363,14 @@ function testFieldAttachSaveMissingDataDefaultValue() {
field_attach_insert($entity);
$entity = clone($entity_init);
$entity->{$this->field_name} = array();
field_attach_load($entity_type, array($entity->ftid => $entity));
$values = field_test_default_value($entity, $this->field, $this->instance);
$this->assertEqual($entity->{$this->field_name}[$langcode], $values, 'Insert: missing field results in default value saved');
$this->assertEqual($entity->{$this->field_name}[$langcode], $default, 'Insert: missing field results in default value saved');
// Verify that prepopulated field values are not overwritten by defaults.
$value = array(array('value' => $default[0]['value'] - mt_rand(1, 127)));
$entity = entity_create('test_entity', array('fttype' => $entity_init->bundle(), $this->field_name => array($langcode => $value)));
$this->assertEqual($entity->{$this->field_name}[$langcode], $value, 'Prepopulated field value correctly maintained.');
}
/**
......
......@@ -69,7 +69,6 @@ function testFieldFormSingle() {
$this->assertText($token_description, 'Token replacement for description is displayed');
$this->assertFieldByName("{$this->field_name}[$langcode][0][value]", '', 'Widget is displayed');
$this->assertNoField("{$this->field_name}[$langcode][1][value]", 'No extraneous widget is displayed');
// TODO : check that the widget is populated with default value ?
// Check that hook_field_widget_form_alter() does not believe this is the
// default value form.
......@@ -113,7 +112,34 @@ function testFieldFormSingle() {
entity_get_controller('test_entity')->resetCache(array($id));
$entity = field_test_entity_test_load($id);
$this->assertIdentical($entity->{$this->field_name}, array(), 'Field was emptied');
}
/**
* Tests field widget default values on entity forms.
*/
function testFieldFormDefaultValue() {
$this->field = $this->field_single;
$this->field_name = $this->field['field_name'];
$this->instance['field_name'] = $this->field_name;
$default = rand(1, 127);
$this->instance['default_value'] = array(array('value' => $default));
field_create_field($this->field);
field_create_instance($this->instance);
$langcode = LANGUAGE_NOT_SPECIFIED;
// Display creation form.
$this->drupalGet('test-entity/add/test_bundle');
// Test that the default value is displayed correctly.
$this->assertFieldByXpath("//input[@name='{$this->field_name}[$langcode][0][value]' and @value='$default']");
// Try to submit an empty value.
$edit = array("{$this->field_name}[$langcode][0][value]" => '');
$this->drupalPost(NULL, $edit, t('Save'));
preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
$id = $match[1];
$this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created.');
$entity = field_test_entity_test_load($id);
$this->assertTrue(empty($entity->{$this->field_name}), 'Field is now empty.');
}
function testFieldFormSingleRequired() {
......
......@@ -242,6 +242,52 @@ function testTranslatableFieldSaveLoad() {
}
$this->assertTrue($result, format_string('%language translation correctly handled.', array('%language' => $langcode)));
}
// Test default values.
$field_name_default = drupal_strtolower($this->randomName() . '_field_name');
$field = $this->field;
$field['field_name'] = $field_name_default;
$instance = $this->instance;
$instance['field_name'] = $field_name_default;
$default = rand(1, 127);
$instance['default_value'] = array(array('value' => $default));
field_create_field($field);
field_create_instance($instance);
$translation_langcodes = array_slice($available_langcodes, 0, 2);
asort($translation_langcodes);
$translation_langcodes = array_values($translation_langcodes);
$eid++;
$evid++;
$values = array('eid' => $eid, 'evid' => $evid, 'fttype' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
foreach ($translation_langcodes as $langcode) {
$values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
}
$entity = entity_create($entity_type, $values);
ksort($entity->{$field_name_default});
$field_langcodes = array_keys($entity->{$field_name_default});
$this->assertEqual($translation_langcodes, $field_langcodes, 'Missing translations did not get a default value.');
foreach ($entity->{$field_name_default} as $langcode => $items) {
$this->assertEqual($items, $instance['default_value'], format_string('Default value correctly populated for language %language.', array('%language' => $langcode)));
}
// Check that explicit empty values are not overridden with default values.
foreach (array(NULL, array()) as $empty_items) {
$eid++;
$evid++;
$values = array('eid' => $eid, 'evid' => $evid, 'fttype' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
foreach ($translation_langcodes as $langcode) {
$values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
$values[$field_name_default][$langcode] = $empty_items;
}
$entity = entity_create($entity_type, $values);
foreach ($entity->{$field_name_default} as $langcode => $items) {
$this->assertEqual($items, $empty_items, format_string('Empty value correctly populated for language %language.', array('%language' => $langcode)));
}
}
}
/**
......
......@@ -121,7 +121,7 @@ function field_test_delete_bundle($bundle) {
* Creates a basic test_entity entity.
*/
function field_test_create_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $label = '') {
$entity = entity_create('test_entity', array());
$entity = entity_create('test_entity', array('fttype' => $bundle));
// Only set id and vid properties if they don't come as NULL (creation form).
if (isset($id)) {
$entity->ftid = $id;
......@@ -131,7 +131,6 @@ function field_test_create_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $l
// Flag to make sure that the provided vid is used for a new revision.
$entity->use_provided_revision_id = $vid;
}
$entity->fttype = $bundle;
$label = !empty($label) ? $label : $bundle . ' label';
$entity->ftlabel = $label;
......
......@@ -6,6 +6,21 @@
*/
/**
* Act on a newly created file.
*
* This hook runs after a new file object has just been instantiated. It can be
* used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\file\Plugin\Core\Entity\File $file
* The file object.
*/
function hook_file_create(\Drupal\file\Plugin\Core\Entity\File $file) {
if (!isset($file->foo)) {
$file->foo = 'some_initial_value';
}
}
/**
* Load additional information into file entities.
*
......
......@@ -104,6 +104,7 @@ public function testActiveForumTopicsBlock() {
$node = $this->drupalGetNodeByTitle($topics[$index]);
$comment = entity_create('comment', array(
'nid' => $node->nid,
'node_type' => 'node_type_' . $node->bundle(),
'subject' => $this->randomString(20),
'comment_body' => $this->randomString(256),
'created' => $timestamp + $index,
......
......@@ -47,6 +47,7 @@ public function testCommentPager() {
for ($i = 0; $i < 60; $i++) {
$comment = entity_create('comment', array(
'nid' => $node->nid,
'node_type' => 'node_type_' . $node->bundle(),
'subject' => $this->randomName(),
'comment_body' => array(
array('value' => $this->randomName()),
......
......@@ -16,9 +16,9 @@
* node.module (for content types created in the user interface) or the module
* that implements hook_node_info() to define the content type.
*
* During node operations (create, update, view, delete, etc.), there are
* several sets of hooks that get invoked to allow modules to modify the base
* node operation:
* During node operations (create, insert, update, view, delete, etc.), there
* are several sets of hooks that get invoked to allow modules to modify the
* base node operation:
* - Node-type-specific hooks: These hooks are only invoked on the primary
* module, using the "base" return component of hook_node_info() as the
* function prefix. For example, poll.module defines the base for the Poll
......@@ -36,6 +36,9 @@
*
* Here is a list of the node and entity hooks that are invoked, field
* operations, and other steps that take place during node operations:
* - Instantiating a new node:
* - hook_node_create() (all)
* - hook_entity_create() (all)
* - Creating a new node (calling node_save() on a new node):
* - field_attach_presave()
* - hook_node_presave() (all)
......@@ -534,6 +537,23 @@ function hook_node_insert(Drupal\node\Node $node) {
->execute();
}
/**
* Act on a newly created node.
*
* This hook runs after a new node object has just been instantiated. It can be
* used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\node\Plugin\Core\Entity\Node $node
* The node object.
*
* @ingroup node_api_hooks
*/
function hook_node_create(\Drupal\node\Plugin\Core\Entity\Node $node) {
if (!isset($node->foo)) {
$node->foo = 'some_initial_value';
}
}
/**
* Act on arbitrary nodes being loaded from the database.
*
......
......@@ -86,6 +86,7 @@ public function testCommentHooks() {
$nid = $node->nid;
$comment = entity_create('comment', array(
'node_type' => 'node_type_' . $node->bundle(),
'cid' => NULL,
'pid' => 0,
'nid' => $nid,
......
......@@ -12,6 +12,21 @@
* @{
*/
/**
* Act on a newly created vocabulary.
*
* This hook runs after a new vocabulary object has just been instantiated. It
* can be used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
* The vocabulary object.
*/
function hook_taxonomy_vocabulary_create(\Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary) {
if (!isset($vocabulary->synonyms)) {
$vocabulary->synonyms = FALSE;
}
}
/**
* Act on taxonomy vocabularies when loaded.
*
......@@ -109,6 +124,21 @@ function hook_taxonomy_vocabulary_delete(Drupal\taxonomy\Plugin\Core\Entity\Voca
}
}
/**
* Act on a newly created term.
*
* This hook runs after a new term object has just been instantiated. It can be
* used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\taxonomy\Plugin\Core\Entity\Term $term
* The term object.
*/
function hook_taxonomy_term_create(\Drupal\taxonomy\Plugin\Core\Entity\Term $term) {
if (!isset($term->foo)) {
$term->foo = 'some_initial_value';
}
}
/**
* Act on taxonomy terms when loaded.
*
......
......@@ -12,6 +12,21 @@
* @{
*/
/**
* Act on a newly created user.
*
* This hook runs after a new user object has just been instantiated. It can be
* used to set initial values, e.g. to provide defaults.
*
* @param \Drupal\user\Plugin\Core\Entity\User $user
* The user object.
*/
function hook_user_create(\Drupal\user\Plugin\Core\Entity\User $user) {
if (!isset($user->foo)) {
$user->foo = 'some_initial_value';
}
}
/**
* Act on user objects when loaded from the database.
*
......
......@@ -108,6 +108,7 @@ protected function setUp() {
$comment = array(
'uid' => $user->uid,
'nid' => $node->nid,
'node_type' => 'node_type_' . $node->bundle(),
);
entity_create('comment', $comment)->save();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment