drupalPlaceBlock('system_breadcrumb_block');
// node_access_test requires a node_access_rebuild().
node_access_rebuild();
// 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'));
}
/**
* Creates a new book with a page hierarchy.
*/
function createBook() {
// Create new book.
$this->drupalLogin($this->bookAuthor);
$this->book = $this->createBookNode('new');
$book = $this->book;
/*
* Add page hierarchy to book.
* Book
* |- Node 0
* |- Node 1
* |- Node 2
* |- Node 3
* |- Node 4
*/
$nodes = array();
$nodes[] = $this->createBookNode($book->id()); // Node 0.
$nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid']); // Node 1.
$nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid']); // Node 2.
$nodes[] = $this->createBookNode($book->id()); // Node 3.
$nodes[] = $this->createBookNode($book->id()); // Node 4.
$this->drupalLogout();
return $nodes;
}
/**
* Tests saving the book outline on an empty book.
*/
function testEmptyBook() {
// Create a new empty book.
$this->drupalLogin($this->bookAuthor);
$book = $this->createBookNode('new');
$this->drupalLogout();
// Log in as a user with access to the book outline and save the form.
$this->drupalLogin($this->adminUser);
$this->drupalPostForm('admin/structure/book/' . $book->id(), array(), t('Save book pages'));
$this->assertText(t('Updated book @book.', array('@book' => $book->label())));
}
/**
* Tests book functionality through node interfaces.
*/
function testBook() {
// Create new book.
$nodes = $this->createBook();
$book = $this->book;
$this->drupalLogin($this->webUser);
// Check that book pages display along with the correct outlines and
// previous/next links.
$this->checkBookNode($book, array($nodes[0], $nodes[3], $nodes[4]), FALSE, FALSE, $nodes[0], array());
$this->checkBookNode($nodes[0], array($nodes[1], $nodes[2]), $book, $book, $nodes[1], array($book));
$this->checkBookNode($nodes[1], NULL, $nodes[0], $nodes[0], $nodes[2], array($book, $nodes[0]));
$this->checkBookNode($nodes[2], NULL, $nodes[1], $nodes[0], $nodes[3], array($book, $nodes[0]));
$this->checkBookNode($nodes[3], NULL, $nodes[2], $book, $nodes[4], array($book));
$this->checkBookNode($nodes[4], NULL, $nodes[3], $book, FALSE, array($book));
$this->drupalLogout();
$this->drupalLogin($this->bookAuthor);
// Check the presence of expected cache tags.
$this->drupalGet('node/add/book');
$this->assertCacheTag('config:book.settings');
/*
* Add Node 5 under Node 3.
* Book
* |- Node 0
* |- Node 1
* |- Node 2
* |- Node 3
* |- Node 5
* |- Node 4
*/
$nodes[] = $this->createBookNode($book->id(), $nodes[3]->book['nid']); // Node 5.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
// Verify the new outline - make sure we don't get stale cached data.
$this->checkBookNode($nodes[3], array($nodes[5]), $nodes[2], $book, $nodes[5], array($book));
$this->checkBookNode($nodes[4], NULL, $nodes[5], $book, FALSE, array($book));
$this->drupalLogout();
// Create a second book, and move an existing book page into it.
$this->drupalLogin($this->bookAuthor);
$other_book = $this->createBookNode('new');
$node = $this->createBookNode($book->id());
$edit = array('book[bid]' => $other_book->id());
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
$this->drupalLogout();
$this->drupalLogin($this->webUser);
// Check that the nodes in the second book are displayed correctly.
// First we must set $this->book to the second book, so that the
// correct regex will be generated for testing the outline.
$this->book = $other_book;
$this->checkBookNode($other_book, array($node), FALSE, FALSE, $node, array());
$this->checkBookNode($node, NULL, $other_book, $other_book, FALSE, array($other_book));
// Test that we can save a book programatically.
$this->drupalLogin($this->bookAuthor);
$book = $this->createBookNode('new');
$book->save();
}
/**
* Checks the outline of sub-pages; previous, up, and next.
*
* Also checks the printer friendly version of the outline.
*
* @param \Drupal\Core\Entity\EntityInterface $node
* Node to check.
* @param $nodes
* Nodes that should be in outline.
* @param $previous
* (optional) Previous link node. Defaults to FALSE.
* @param $up
* (optional) Up link node. Defaults to FALSE.
* @param $next
* (optional) Next link node. Defaults to FALSE.
* @param array $breadcrumb
* The nodes that should be displayed in the breadcrumb.
*/
function checkBookNode(EntityInterface $node, $nodes, $previous = FALSE, $up = FALSE, $next = FALSE, array $breadcrumb) {
// $number does not use drupal_static as it should not be reset
// since it uniquely identifies each call to checkBookNode().
static $number = 0;
$this->drupalGet('node/' . $node->id());
// Check outline structure.
if ($nodes !== NULL) {
$this->assertPattern($this->generateOutlinePattern($nodes), format_string('Node @number outline confirmed.', array('@number' => $number)));
}
else {
$this->pass(format_string('Node %number does not have outline.', array('%number' => $number)));
}
// Check previous, up, and next links.
if ($previous) {
/** @var \Drupal\Core\Url $url */
$url = $previous->urlInfo();
$url->setOptions(array('attributes' => array('rel' => array('prev'), 'title' => t('Go to previous page'))));
$text = SafeMarkup::format('‹ @label', array('@label' => $previous->label()));
$this->assertRaw(\Drupal::l($text, $url), 'Previous page link found.');
}
if ($up) {
/** @var \Drupal\Core\Url $url */
$url = $up->urlInfo();
$url->setOptions(array('attributes' => array('title' => t('Go to parent page'))));
$this->assertRaw(\Drupal::l('Up', $url), 'Up page link found.');
}
if ($next) {
/** @var \Drupal\Core\Url $url */
$url = $next->urlInfo();
$url->setOptions(array('attributes' => array('rel' => array('next'), 'title' => t('Go to next page'))));
$text = SafeMarkup::format('@label ›', array('@label' => $next->label()));
$this->assertRaw(\Drupal::l($text, $url), 'Next page link found.');
}
// Compute the expected breadcrumb.
$expected_breadcrumb = array();
$expected_breadcrumb[] = \Drupal::url('');
foreach ($breadcrumb as $a_node) {
$expected_breadcrumb[] = $a_node->url();
}
// Fetch links in the current breadcrumb.
$links = $this->xpath('//nav[@class="breadcrumb"]/ol/li/a');
$got_breadcrumb = array();
foreach ($links as $link) {
$got_breadcrumb[] = (string) $link['href'];
}
// Compare expected and got breadcrumbs.
$this->assertIdentical($expected_breadcrumb, $got_breadcrumb, 'The breadcrumb is correctly displayed on the page.');
// Check printer friendly version.
$this->drupalGet('book/export/html/' . $node->id());
$this->assertText($node->label(), 'Printer friendly title found.');
$this->assertRaw($node->body->processed, 'Printer friendly body found.');
$number++;
}
/**
* Creates a regular expression to check for the sub-nodes in the outline.
*
* @param array $nodes
* An array of nodes to check in outline.
*
* @return string
* A regular expression that locates sub-nodes of the outline.
*/
function generateOutlinePattern($nodes) {
$outline = '';
foreach ($nodes as $node) {
$outline .= '(node\/' . $node->id() . ')(.*?)(' . $node->label() . ')(.*?)';
}
return '/