diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php index 3e14c40..17bc0d9 100644 --- a/core/modules/book/src/BookManager.php +++ b/core/modules/book/src/BookManager.php @@ -249,6 +249,13 @@ public function updateOutline(NodeInterface $node) { return FALSE; } + // Prevent changes to the book outline if the node being saved is not the + // default revision. + if (!$node->isDefaultRevision()) { + drupal_set_message($this->t('The book outline could not be changed because a non-default revision (for example: a new draft revision) of the content has been created.'), 'error'); + return FALSE; + } + if (!empty($node->book['bid'])) { if ($node->book['bid'] == 'new') { // New nodes that are their own book. diff --git a/core/modules/book/tests/src/Functional/BookContentModerationTest.php b/core/modules/book/tests/src/Functional/BookContentModerationTest.php new file mode 100644 index 0000000..6838297 --- /dev/null +++ b/core/modules/book/tests/src/Functional/BookContentModerationTest.php @@ -0,0 +1,88 @@ +getTypePlugin()->addEntityTypeAndBundle('node', 'book'); + $workflow->save(); + + // We need a user with additional content moderation permissions. + $this->bookAuthor = $this->drupalCreateUser(['create new books', 'create book content', 'edit own book content', 'add content to books', 'access printer-friendly version', 'view any unpublished content', 'use editorial transition create_new_draft', 'use editorial transition publish']); + } + + /** + * Tests that book drafts can not modify the book outline. + */ + public function testBookWithForwardRevisions() { + // Create two books. + $book_1_nodes = $this->createBook(); + $book_1 = $this->book; + + $this->createBook(); + $book_2 = $this->book; + + $this->drupalLogin($this->bookAuthor); + + // Check that book pages display along with the correct outlines. + $this->book = $book_1; + $this->checkBookNode($book_1, [$book_1_nodes[0], $book_1_nodes[3], $book_1_nodes[4]], FALSE, FALSE, $book_1_nodes[0], []); + $this->checkBookNode($book_1_nodes[0], [$book_1_nodes[1], $book_1_nodes[2]], $book_1, $book_1, $book_1_nodes[1], [$book_1]); + + // Try to move Node 2 to a different parent. + $edit['book[pid]'] = $book_1_nodes[3]->id(); + $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save and Create New Draft')); + + $this->assertSession()->pageTextContains('The book outline could not be changed because a non-default revision (for example: a new draft revision) of the content has been created.'); + + // Check that the book outline did not change. + $this->book = $book_1; + $this->checkBookNode($book_1, [$book_1_nodes[0], $book_1_nodes[3], $book_1_nodes[4]], FALSE, FALSE, $book_1_nodes[0], []); + $this->checkBookNode($book_1_nodes[0], [$book_1_nodes[1], $book_1_nodes[2]], $book_1, $book_1, $book_1_nodes[1], [$book_1]); + + // Try to move Node 2 to a different book. + $edit['book[bid]'] = $book_2->id(); + $this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save and Create New Draft')); + + $this->assertSession()->pageTextContains('The book outline could not be changed because a non-default revision (for example: a new draft revision) of the content has been created.'); + + // Check that the book outline did not change. + $this->book = $book_1; + $this->checkBookNode($book_1, [$book_1_nodes[0], $book_1_nodes[3], $book_1_nodes[4]], FALSE, FALSE, $book_1_nodes[0], []); + $this->checkBookNode($book_1_nodes[0], [$book_1_nodes[1], $book_1_nodes[2]], $book_1, $book_1, $book_1_nodes[1], [$book_1]); + + } + + /** + * {@inheritdoc} + */ + public function createBookNode($book_nid, $parent = NULL, $submit = NULL) { + // This test has Content Moderation enabled so we have a different value for + // the submit button. + $submit = t('Save and Publish'); + + return parent::createBookNode($book_nid, $parent, $submit); + } + +} diff --git a/core/modules/book/tests/src/Kernel/BookForwardRevisionTest.php b/core/modules/book/tests/src/Kernel/BookForwardRevisionTest.php new file mode 100644 index 0000000..c15aaf8 --- /dev/null +++ b/core/modules/book/tests/src/Kernel/BookForwardRevisionTest.php @@ -0,0 +1,79 @@ +installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installSchema('book', ['book']); + $this->installSchema('node', ['node_access']); + $this->installConfig(['node', 'book', 'field']); + } + + /** + * Tests the book_system_info_alter() method. + */ + public function testBookWithForwardRevisions() { + $content_type = NodeType::create([ + 'type' => $this->randomMachineName(), + 'name' => $this->randomString(), + ]); + $content_type->save(); + $book_config = $this->config('book.settings'); + $allowed_types = $book_config->get('allowed_types'); + $allowed_types[] = $content_type->id(); + $book_config->set('allowed_types', $allowed_types)->save(); + + // Create two top-level books a child. + $book_1 = Node::create(['title' => $this->randomString(), 'type' => $content_type->id()]); + $book_1->book['bid'] = 'new'; + $book_1->save(); + + $book_2 = Node::create(['title' => $this->randomString(), 'type' => $content_type->id()]); + $book_2->book['bid'] = 'new'; + $book_2->save(); + + $child = Node::create(['title' => $this->randomString(), 'type' => $content_type->id()]); + $child->book['bid'] = $book_1->id(); + $child->book['pid'] = $book_1->id(); + $child->save(); + + // Try to move the child to a different book while saving it as a forward + // revision. + /** @var \Drupal\book\BookManagerInterface $book_manager */ + $book_manager = $this->container->get('book.manager'); + + // First, check that the API doesn't allow us to change the book outline for + // forward revisions. + $child->book['bid'] = $book_2->id(); + $child->setNewRevision(TRUE); + $child->isDefaultRevision(FALSE); + + $return = $book_manager->updateOutline($child); + $this->assertFalse($return, 'A forward revision can not change the book outline.'); + } + +}