diff --git a/core/modules/book/book.module b/core/modules/book/book.module index fe6e06d6fa9..51da82352da 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -238,6 +238,10 @@ function book_node_load($nodes) { function book_node_view(array &$build, EntityInterface $node, EntityViewDisplayInterface $display, $view_mode) { if ($view_mode == 'full') { if (!empty($node->book['bid']) && empty($node->in_preview)) { + $book_node = Node::load($node->book['bid']); + if (!$book_node->access()) { + return; + } $book_navigation = array( '#theme' => 'book_navigation', '#book_link' => $node->book); $build['book_navigation'] = array( '#markup' => drupal_render($book_navigation), diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php index 3f5cfdca4cf..376cc69b604 100644 --- a/core/modules/book/src/BookManager.php +++ b/core/modules/book/src/BookManager.php @@ -695,7 +695,7 @@ class BookManager implements BookManagerInterface { * {@inheritdoc} */ public function loadBookLinks($nids, $translate = TRUE) { - $result = $this->bookOutlineStorage->loadMultiple($nids); + $result = $this->bookOutlineStorage->loadMultiple($nids, $translate); $links = array(); foreach ($result as $link) { if ($translate) { diff --git a/core/modules/book/src/BookManagerInterface.php b/core/modules/book/src/BookManagerInterface.php index 72ca61ea82a..dd2ba6b4fea 100644 --- a/core/modules/book/src/BookManagerInterface.php +++ b/core/modules/book/src/BookManagerInterface.php @@ -57,6 +57,12 @@ interface BookManagerInterface { /** * Loads a single book entry. * + * The entries of a book entry is documented in + * \Drupal\book\BookOutlineStorageInterface::loadMultiple. + * + * If $translate is TRUE, it also checks access ('access' key) and + * loads the title from the node itself. + * * @param int $nid * The node ID of the book. * @param bool $translate @@ -64,12 +70,20 @@ interface BookManagerInterface { * * @return array * The book data of that node. + * + * @see \Drupal\book\BookOutlineStorageInterface::loadMultiple */ public function loadBookLink($nid, $translate = TRUE); /** * Loads multiple book entries. * + * The entries of a book entry is documented in + * \Drupal\book\BookOutlineStorageInterface::loadMultiple. + * + * If $translate is TRUE, it also checks access ('access' key) and + * loads the title from the node itself. + * * @param int[] $nids * An array of nids to load. * @@ -78,6 +92,8 @@ interface BookManagerInterface { * * @return array[] * The book data of each node keyed by NID. + * + * @see \Drupal\book\BookOutlineStorageInterface::loadMultiple */ public function loadBookLinks($nids, $translate = TRUE); diff --git a/core/modules/book/src/BookOutlineStorage.php b/core/modules/book/src/BookOutlineStorage.php index 8e8271d2364..9af8a60ddb8 100644 --- a/core/modules/book/src/BookOutlineStorage.php +++ b/core/modules/book/src/BookOutlineStorage.php @@ -47,12 +47,16 @@ class BookOutlineStorage implements BookOutlineStorageInterface { /** * {@inheritdoc} */ - public function loadMultiple($nids) { + public function loadMultiple($nids, $access = TRUE) { $query = $this->connection->select('book', 'b', array('fetch' => \PDO::FETCH_ASSOC)); $query->fields('b'); $query->condition('b.nid', $nids); - $query->addTag('node_access'); - $query->addMetaData('base_table', 'book'); + + if ($access) { + $query->addTag('node_access'); + $query->addMetaData('base_table', 'book'); + } + return $query->execute(); } diff --git a/core/modules/book/src/BookOutlineStorageInterface.php b/core/modules/book/src/BookOutlineStorageInterface.php index e08fab6d8a3..9c94412b18e 100644 --- a/core/modules/book/src/BookOutlineStorageInterface.php +++ b/core/modules/book/src/BookOutlineStorageInterface.php @@ -31,13 +31,23 @@ interface BookOutlineStorageInterface { /** * Loads books. * + * Each book entry consists of the following keys: + * - bid: The node ID of the main book. + * - nid: The node ID of the book entry itself. + * - pid: The parent node ID of the book. + * - has_children: A boolean to indicate whether the book has children. + * - weight: The weight of the book entry to order siblings. + * - depth: The depth in the menu hierarchy the entry is placed into. + * * @param array $nids * An array of node IDs. + * @param bool $access + * Whether access checking should be taken into account. * * @return array * Array of loaded book items. */ - public function loadMultiple($nids); + public function loadMultiple($nids, $access = TRUE); /** * Gets child relative depth. diff --git a/core/modules/book/src/Tests/BookTest.php b/core/modules/book/src/Tests/BookTest.php index 0064a66283b..0f88dc82f71 100644 --- a/core/modules/book/src/Tests/BookTest.php +++ b/core/modules/book/src/Tests/BookTest.php @@ -52,6 +52,13 @@ class BookTest extends WebTestBase { */ protected $adminUser; + /** + * A user without the 'node test view' permission. + * + * @var \Drupal\user\UserInterface + */ + protected $webUserWithoutNodeAccess; + protected function setUp() { parent::setUp(); @@ -61,6 +68,7 @@ class BookTest extends WebTestBase { // Create users. $this->bookAuthor = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books')); $this->webUser = $this->drupalCreateUser(array('access printer-friendly version', 'node test view')); + $this->webUserWithoutNodeAccess = $this->drupalCreateUser(array('access printer-friendly version')); $this->adminUser = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks', 'administer permissions', 'administer book outlines', 'node test view', 'administer content types', 'administer site configuration')); } @@ -660,4 +668,35 @@ class BookTest extends WebTestBase { $this->assertText($this->book->label(), 'The book title is displayed on the administrative book listing page.'); } + /** + * Ensure the loaded book in hook_node_load() does not depend on the user. + */ + public function testHookNodeLoadAccess() { + \Drupal::service('module_installer')->install(['node_access_test']); + + // Ensure that the loaded book in hook_node_load() does NOT depend on the + // current user. + $this->drupalLogin($this->bookAuthor); + $this->book = $this->createBookNode('new'); + // Reset any internal static caching. + $node_storage = \Drupal::entityManager()->getStorage('node'); + $node_storage->resetCache(); + + // Login as user without access to the book node, so no 'node test view' + // permission. + // @see node_access_test_node_grants(). + $this->drupalLogin($this->webUserWithoutNodeAccess); + $book_node = $node_storage->load($this->book->id()); + $this->assertTrue(!empty($book_node->book)); + $this->assertEqual($book_node->book['bid'], $this->book->id()); + + // Reset the internal cache to retrigger the hook_node_load() call. + $node_storage->resetCache(); + + $this->drupalLogin($this->webUser); + $book_node = $node_storage->load($this->book->id()); + $this->assertTrue(!empty($book_node->book)); + $this->assertEqual($book_node->book['bid'], $this->book->id()); + } + }