From 62c93ff8d418876d24f5d0031327b7b93cfde23f Mon Sep 17 00:00:00 2001 From: webchick <webchick@24967.no-reply.drupal.org> Date: Wed, 16 Jan 2013 09:37:23 -0800 Subject: [PATCH] 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. --- core/includes/entity.api.php | 17 +++++++ .../Core/Entity/DatabaseStorageController.php | 4 ++ core/lib/Drupal/Core/Entity/Entity.php | 7 ++- core/lib/Drupal/Core/Entity/EntityNG.php | 3 +- core/modules/comment/comment.api.php | 15 ++++++ .../comment/Tests/CommentApprovalTest.php | 4 +- .../Drupal/comment/Tests/CommentCSSTest.php | 1 + .../comment/Tests/Views/CommentTestBase.php | 1 + core/modules/field/field.attach.inc | 1 - core/modules/field/field.default.inc | 31 ------------- core/modules/field/field.module | 37 +++++++++++++++ .../field/Plugin/Type/Widget/WidgetBase.php | 5 -- .../field/Tests/FieldAttachStorageTest.php | 13 +++++- .../field/lib/Drupal/field/Tests/FormTest.php | 28 ++++++++++- .../Drupal/field/Tests/TranslationTest.php | 46 +++++++++++++++++++ .../modules/field_test/field_test.entity.inc | 3 +- core/modules/file/file.api.php | 15 ++++++ .../lib/Drupal/forum/Tests/ForumBlockTest.php | 1 + .../Drupal/node/Tests/NodeAccessPagerTest.php | 1 + core/modules/node/node.api.php | 26 +++++++++-- .../Tests/Entity/EntityCrudHookTest.php | 1 + core/modules/taxonomy/taxonomy.api.php | 30 ++++++++++++ core/modules/user/user.api.php | 15 ++++++ .../Drupal/views/Tests/DefaultViewsTest.php | 1 + 24 files changed, 256 insertions(+), 50 deletions(-) diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php index 7b42253bfee7..88e370a3cc11 100644 --- a/core/includes/entity.api.php +++ b/core/includes/entity.api.php @@ -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. * diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index e6d8fe6fbea6..6ccea0e171c8 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -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; } diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php index adbb0476b70c..5660a4da197e 100644 --- a/core/lib/Drupal/Core/Entity/Entity.php +++ b/core/lib/Drupal/Core/Entity/Entity.php @@ -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; } /** diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php index b798bcf898e3..99a29ca446e3 100644 --- a/core/lib/Drupal/Core/Entity/EntityNG.php +++ b/core/lib/Drupal/Core/Entity/EntityNG.php @@ -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; } diff --git a/core/modules/comment/comment.api.php b/core/modules/comment/comment.api.php index 1762c6998335..cb4133d75129 100644 --- a/core/modules/comment/comment.api.php +++ b/core/modules/comment/comment.api.php @@ -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. * diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php index e123fc439e63..5c3f15758425 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php @@ -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.'); diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentCSSTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentCSSTest.php index 5cf0720a6b00..1294de4ff922 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentCSSTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentCSSTest.php @@ -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(), diff --git a/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php index 476c0eff10e0..c9138839d9c1 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/Views/CommentTestBase.php @@ -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(); diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc index 9748b6beed4a..f9db7b5f6fde 100644 --- a/core/modules/field/field.attach.inc +++ b/core/modules/field/field.attach.inc @@ -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 diff --git a/core/modules/field/field.default.inc b/core/modules/field/field.default.inc index 91682650806b..27ba32c73140 100644 --- a/core/modules/field/field.default.inc +++ b/core/modules/field/field.default.inc @@ -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. * diff --git a/core/modules/field/field.module b/core/modules/field/field.module index df37a5b79a68..e30f16a3fbe3 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -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. */ diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php index c4e1e26a2404..4fb8d5b544e1 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php @@ -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( diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php index 1de9ffa7147d..5ae36108a801 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php @@ -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.'); } /** diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php index 4df7218b6165..77ac613352d6 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php @@ -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() { diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php index 8930d97d0f64..f2ada97de478 100644 --- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php @@ -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))); + } + } } /** diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc index 2a7f29598f3a..aeb1634feb4f 100644 --- a/core/modules/field/tests/modules/field_test/field_test.entity.inc +++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc @@ -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; diff --git a/core/modules/file/file.api.php b/core/modules/file/file.api.php index fb59229b977b..a6d11d5ea1cb 100644 --- a/core/modules/file/file.api.php +++ b/core/modules/file/file.api.php @@ -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. * diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php index 7f5405d6cde0..260b23043faf 100644 --- a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php +++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php @@ -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, diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php index 4044bdcef435..de24f451c93d 100644 --- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php +++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php @@ -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()), diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php index 2660e25fe0f9..10d32d0a52e6 100644 --- a/core/modules/node/node.api.php +++ b/core/modules/node/node.api.php @@ -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. * diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php index 8f16e45a836a..f79512c06912 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php @@ -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, diff --git a/core/modules/taxonomy/taxonomy.api.php b/core/modules/taxonomy/taxonomy.api.php index 956b8b4d07bc..d2f41f661f8d 100644 --- a/core/modules/taxonomy/taxonomy.api.php +++ b/core/modules/taxonomy/taxonomy.api.php @@ -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. * diff --git a/core/modules/user/user.api.php b/core/modules/user/user.api.php index 38e317091b7d..cae3ddec91af 100644 --- a/core/modules/user/user.api.php +++ b/core/modules/user/user.api.php @@ -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. * diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php index 1e5b72ffac2c..01eaca3aa950 100644 --- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php @@ -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(); } -- GitLab