diff --git a/core/modules/book/book.module b/core/modules/book/book.module index a6c1886..c19ce7e 100644 --- a/core/modules/book/book.module +++ b/core/modules/book/book.module @@ -221,7 +221,17 @@ function _book_outline_access($node) { * @see book_menu() */ function _book_outline_remove_access($node) { - return isset($node->book) && ($node->book['bid'] != $node->nid) && _book_outline_access($node); + return _book_node_is_removable($node) && _book_outline_access($node); +} + +/** + * Determines if a node can be removed from the book. + * + * A node can be removed from a book if it is actually in a book and it either + * is not a top-level page or is a top-level page with no children. + */ +function _book_node_is_removable($node) { + return (!empty($node->book['bid']) && (($node->book['bid'] != $node->nid) || !$node->book['has_children'])); } /** diff --git a/core/modules/book/book.pages.inc b/core/modules/book/book.pages.inc index e5f0832..98282be 100644 --- a/core/modules/book/book.pages.inc +++ b/core/modules/book/book.pages.inc @@ -149,7 +149,7 @@ function book_outline_form($form, &$form_state, $node) { $form['remove'] = array( '#type' => 'submit', '#value' => t('Remove from book outline'), - '#access' => $node->nid != $node->book['bid'] && $node->book['bid'], + '#access' => _book_node_is_removable($node), '#weight' => 20, '#submit' => array('book_remove_button_submit'), ); @@ -231,8 +231,7 @@ function book_remove_form($form, &$form_state, $node) { */ function book_remove_form_submit($form, &$form_state) { $node = $form['#node']; - if ($node->nid != $node->book['bid']) { - // Only allowed when this is not a book (top-level page). + if (_book_node_is_removable($node)) { menu_link_delete($node->book['mlid']); db_delete('book') ->condition('nid', $node->nid) diff --git a/core/modules/book/book.test b/core/modules/book/book.test index 83328e0..33b508f 100644 --- a/core/modules/book/book.test +++ b/core/modules/book/book.test @@ -32,7 +32,7 @@ class BookTestCase extends DrupalWebTestCase { // Create users. $this->book_author = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books')); $this->web_user = $this->drupalCreateUser(array('access printer-friendly version', 'node test view')); - $this->admin_user = $this->drupalCreateUser(array('create new books', 'create book content', 'edit own book content', 'add content to books', 'administer blocks', 'administer permissions')); + $this->admin_user = $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')); } /** @@ -332,4 +332,30 @@ class BookTestCase extends DrupalWebTestCase { $this->drupalGet('node/' . $this->book->nid); $this->assertText($block_title, t('Book navigation block is displayed to anonymous users.')); } + + /** + * Tests the access for deleting top-level book nodes. + */ + function testBookDelete() { + $nodes = $this->createBook(); + $this->drupalLogin($this->admin_user); + $edit = array(); + + // Test access to delete top-level and child book nodes. + $this->drupalGet('node/' . $this->book->nid . '/outline/remove'); + $this->assertResponse('403', t('Deleting top-level book node properly forbidden.')); + $this->drupalPost('node/' . $nodes[4]->nid . '/outline/remove', $edit, t('Remove')); + $node4 = node_load($nodes[4]->nid, NULL, TRUE); + $this->assertTrue(empty($node4->book), t('Deleting child book node properly allowed.')); + + // Delete all child book nodes and retest top-level node deletion. + foreach ($nodes as $node) { + $nids[] = $node->nid; + } + node_delete_multiple($nids); + $this->drupalPost('node/' . $this->book->nid . '/outline/remove', $edit, t('Remove')); + $node = node_load($this->book->nid, NULL, TRUE); + $this->assertTrue(empty($node->book), t('Deleting childless top-level book node properly allowed.')); + } } +