diff --git a/core/modules/node/src/NodeGrantDatabaseStorage.php b/core/modules/node/src/NodeGrantDatabaseStorage.php index a8289edb7edfdaca8e4ce1d67bdd391bbebce4c0..45d9f85546d5c083f5473dfb01b6e1262617df57 100644 --- a/core/modules/node/src/NodeGrantDatabaseStorage.php +++ b/core/modules/node/src/NodeGrantDatabaseStorage.php @@ -82,10 +82,16 @@ public function access(NodeInterface $node, $operation, AccountInterface $accoun $query->addExpression('1'); // Only interested for granting in the current operation. $query->condition('grant_' . $operation, 1, '>='); - // Check for grants for this node and the correct langcode. + // Check for grants for this node and the correct langcode. New translations + // do not yet have a langcode and must check the fallback node record. $nids = $query->andConditionGroup() - ->condition('nid', $node->id()) - ->condition('langcode', $node->language()->getId()); + ->condition('nid', $node->id()); + if (!$node->isNewTranslation()) { + $nids->condition('langcode', $node->language()->getId()); + } + else { + $nids->condition('fallback', 1); + } // If the node is published, also take the default grant into account. The // default is saved with a node ID of 0. $status = $node->isPublished(); diff --git a/core/modules/path/tests/modules/path_test_node_grants/path_test_node_grants.info.yml b/core/modules/path/tests/modules/path_test_node_grants/path_test_node_grants.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..8839376ae2eb8536515b14fd015930407d4a1b9d --- /dev/null +++ b/core/modules/path/tests/modules/path_test_node_grants/path_test_node_grants.info.yml @@ -0,0 +1,5 @@ +name: 'Path test with node grants' +type: module +description: 'Tests URL alias with hook_node_grants implementation' +package: Testing +version: VERSION diff --git a/core/modules/path/tests/modules/path_test_node_grants/path_test_node_grants.module b/core/modules/path/tests/modules/path_test_node_grants/path_test_node_grants.module new file mode 100644 index 0000000000000000000000000000000000000000..62345733f0d8add08fc7af0e4d61437921337797 --- /dev/null +++ b/core/modules/path/tests/modules/path_test_node_grants/path_test_node_grants.module @@ -0,0 +1,16 @@ +<?php + +/** + * @file + * Contains hook implementations for the Path test with node grants module. + */ + +use Drupal\Core\Session\AccountInterface; + +/** + * Implements hook_node_grants(). + */ +function path_test_node_grants_node_grants(AccountInterface $account, $operation): array { + $grants = []; + return $grants; +} diff --git a/core/modules/path/tests/src/Functional/PathWithNodeAccessGrantsTest.php b/core/modules/path/tests/src/Functional/PathWithNodeAccessGrantsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..54e9ff1d4c81497a59ca38995b3c068ae414704b --- /dev/null +++ b/core/modules/path/tests/src/Functional/PathWithNodeAccessGrantsTest.php @@ -0,0 +1,111 @@ +<?php + +namespace Drupal\Tests\path\Functional; + +use Drupal\Tests\content_moderation\Traits\ContentModerationTestTrait; +use Drupal\Tests\content_translation\Traits\ContentTranslationTestTrait; + +/** + * Confirm that paths work with node access grants implementations. + * + * @group path + */ +class PathWithNodeAccessGrantsTest extends PathTestBase { + + use ContentTranslationTestTrait; + use ContentModerationTestTrait; + + /** + * Modules to enable. + * + * @var array + */ + protected static $modules = [ + 'path', + 'locale', + 'locale_test', + 'content_translation', + 'content_moderation', + 'path_test_node_grants', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Create a workflow for basic page. + $workflow = $this->createEditorialWorkflow(); + $this->addEntityTypeAndBundleToWorkflow($workflow, 'node', 'page'); + + // Login as admin user to configure language detection and selection. + $admin_user = $this->drupalCreateUser([ + 'edit any page content', + 'create page content', + 'administer url aliases', + 'create url aliases', + 'administer languages', + 'access administration pages', + ]); + $this->drupalLogin($admin_user); + // Enable French language. + static::createLanguageFromLangcode('fr'); + // Enable URL language detection and selection. + $edit = ['language_interface[enabled][language-url]' => 1]; + $this->drupalGet('admin/config/regional/language/detection'); + $this->submitForm($edit, 'Save settings'); + // Enable translation for page node. + static::enableContentTranslation('node', 'page'); + static::setFieldTranslatable('node', 'page', 'body', TRUE); + + $definitions = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'page'); + $this->assertTrue($definitions['path']->isTranslatable(), 'Node path is translatable.'); + $this->assertTrue($definitions['body']->isTranslatable(), 'Node body is translatable.'); + } + + /** + * Tests alias functionality through the admin interfaces. + */ + public function testAliasTranslation() : void { + // Rebuild the permissions to update 'node_access' table. + node_access_rebuild(); + $alias = $this->randomMachineName(); + $permissions = [ + 'access administration pages', + 'view any unpublished content', + 'use editorial transition create_new_draft', + 'use editorial transition publish', + 'create content translations', + 'create page content', + 'create url aliases', + 'edit any page content', + 'translate any entity', + ]; + $this->drupalLogin($this->drupalCreateUser($permissions)); + // Create a node, add URL alias and publish it. + $this->drupalGet('node/add/page'); + $edit['title[0][value]'] = 'test'; + $edit['path[0][alias]'] = '/' . $alias; + $edit['moderation_state[0][state]'] = 'published'; + $this->submitForm($edit, 'Save'); + // Add french translation. + $this->drupalGet('node/1/translations'); + $this->clickLink('Add'); + $this->submitForm(['moderation_state[0][state]' => 'published'], 'Save (this translation)'); + // Translation should be saved. + $this->assertSession()->pageTextContains('Basic page test has been updated.'); + // There shouldn't be any validation errors. + $this->assertSession()->pageTextNotContains("Either the path '/node/1' is invalid or you do not have access to it."); + // Translation should be saved with the given alias. + $this->container->get('path_alias.manager')->cacheClear(); + $translation_alias = $this->container->get('path_alias.manager')->getAliasByPath('/node/1', 'fr'); + $this->assertSame('/' . $alias, $translation_alias); + } + +}