diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php index 31128f7..64f7128 100644 --- a/core/modules/book/src/BookManager.php +++ b/core/modules/book/src/BookManager.php @@ -914,9 +914,30 @@ public function bookTreeCheckAccess(&$tree, $node_links = array()) { // @todo This should be actually filtering on the desired node status // field language and just fall back to the default language. - $nids = \Drupal::entityQuery('node') + $query = \Drupal::entityQuery('node'); + + $account = \Drupal::currentUser(); + if (!$account->hasPermission('bypass node access')) { + // If the user is able to view their own unpublished nodes, allow them + // to see these in addition to published nodes. Check that they actually + // have some unpublished nodes to view before adding the condition. + $access_query = \Drupal::entityQuery('node') + ->condition('uid', $account->id()) + ->condition('status', NODE_NOT_PUBLISHED); + if ($account->hasPermission('view own unpublished content') && ($own_unpublished = $access_query->execute())) { + $query->orConditionGroup() + ->condition('status', NODE_PUBLISHED) + ->condition('nid', $own_unpublished, 'IN'); + } + else { + // If not, restrict the query to published nodes. + $query->condition('status', NODE_PUBLISHED); + } + } + + $nids = $query ->condition('nid', $nids, 'IN') - ->condition('status', 1) + ->addTag('node_access') ->execute(); foreach ($nids as $nid) { diff --git a/core/modules/book/src/Tests/BookTest.php b/core/modules/book/src/Tests/BookTest.php index 95b62e3..eb4df2f 100644 --- a/core/modules/book/src/Tests/BookTest.php +++ b/core/modules/book/src/Tests/BookTest.php @@ -44,14 +44,14 @@ class BookTest extends WebTestBase { /** * A user with permission to view a book and access printer-friendly version. * - * @var object + * @var \Drupal\user\UserInterface */ protected $webUser; /** * A user with permission to create and edit books and to administer blocks. * - * @var object + * @var \Drupal\user\UserInterface */ protected $adminUser; @@ -441,6 +441,26 @@ function testBookNavigationBlock() { $this->assertText($block->label(), 'Book navigation block is displayed.'); $this->assertText($this->book->label(), format_string('Link to book root (@title) is displayed.', array('@title' => $nodes[0]->label()))); $this->assertNoText($nodes[0]->label(), 'No links to individual book pages are displayed.'); + + // Ensure that an unpublished node does not appear in the navigation for a + // user without access. By unpublishing a parent page, child pages should + // not appear in the navigation. The node_access_test module is disabled + // since it interfers with this logic. + + /** @var \Drupal\Core\Extension\ModuleInstaller $installer */ + $installer = \Drupal::service('module_installer'); + $installer->uninstall(['node_access_test']); + node_access_rebuild(); + + $nodes[0]->setPublished(FALSE); + $nodes[0]->save(); + $this->drupalLogin($this->webUser); + $this->drupalGet($nodes[0]->urlInfo()); + $this->assertResponse(403); + $this->drupalGet($this->book->urlInfo()); + $this->assertNoText($nodes[0]->getTitle(), 'Unpublished book page does not appear in the navigation for users without access.'); + $this->assertNoText($nodes[1]->getTitle(), 'Published child page does not appear below an unpublished parent.'); + $this->assertNoText($nodes[2]->getTitle(), 'Published child page does not appear below an unpublished parent.'); } /** @@ -685,7 +705,7 @@ public function testSaveBookLink() { */ public function testBookListing() { // Create a new book. - $this->createBook(); + $nodes = $this->createBook(); // Must be a user with 'node test view' permission since node_access_test is installed. $this->drupalLogin($this->webUser); @@ -701,7 +721,7 @@ public function testBookListing() { */ public function testAdminBookListing() { // Create a new book. - $this->createBook(); + $nodes = $this->createBook(); // Load the book page and assert the created book title is displayed. $this->drupalLogin($this->adminUser); @@ -714,7 +734,7 @@ public function testAdminBookListing() { */ public function testAdminBookNodeListing() { // Create a new book. - $this->createBook(); + $nodes = $this->createBook(); $this->drupalLogin($this->adminUser); // Load the book page list and assert the created book title is displayed @@ -724,6 +744,29 @@ public function testAdminBookNodeListing() { $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a'); $this->assertEqual((string) $elements[0], 'View', 'View link is found from the list.'); + $this->assertEqual(count($nodes), count($elements), 'All the book pages are displayed on the book outline page.'); + + // Unpublish a book in the hierarchy. + $nodes[0]->setPublished(FALSE); + $nodes[0]->save(); + + // Node should still appear on the outline for admins. + $this->drupalGet('admin/structure/book/' . $this->book->id()); + $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a'); + $this->assertEqual(count($nodes), count($elements), 'All the book pages are displayed on the book outline page.'); + + // Saving a book page not as the current version shouldn't effect the book. + $old_title = $nodes[1]->getTitle(); + $new_title = $this->randomGenerator->name(); + $nodes[1]->isDefaultRevision(FALSE); + $nodes[1]->setNewRevision(TRUE); + $nodes[1]->setTitle($new_title); + $nodes[1]->save(); + $this->drupalGet('admin/structure/book/' . $this->book->id()); + $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a'); + $this->assertEqual(count($nodes), count($elements), 'All the book pages are displayed on the book outline page.'); + $this->assertNoText($new_title, 'The revision that is not the default revision does not appear.'); + $this->assertText($old_title, 'The default revision title appears.'); } /**