Update to Drupal 8.2.0. For more information, see https://www.drupal.org/project/drupal/releases/8.2.0
This commit is contained in:
parent
2f563ab520
commit
f1c8716f57
1732 changed files with 52334 additions and 11780 deletions
|
@ -3,7 +3,7 @@ administer book outlines:
|
|||
create new books:
|
||||
title: 'Create new books'
|
||||
add content to books:
|
||||
title: 'Add content and child pages to books'
|
||||
title: 'Add content and child pages to books and manage their hierarchies.'
|
||||
access printer-friendly version:
|
||||
title: 'View printer-friendly books'
|
||||
description: 'View a book page and all of its sub-pages as a single document for ease of printing. Can be performance heavy.'
|
||||
|
|
125
core/modules/book/book.views.inc
Normal file
125
core/modules/book/book.views.inc
Normal file
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Provide views data for book.module.
|
||||
*
|
||||
* @ingroup views_module_handlers
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements hook_views_data().
|
||||
*/
|
||||
function book_views_data() {
|
||||
$data = [];
|
||||
$data['book'] = [];
|
||||
$data['book']['table'] = [];
|
||||
$data['book']['table']['group'] = t('Book');
|
||||
|
||||
$data['book']['table']['join'] = [
|
||||
'node_field_data' => [
|
||||
'left_field' => 'nid',
|
||||
'field' => 'nid',
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['nid'] = [
|
||||
'title' => t('Page'),
|
||||
'help' => t('The book page node.'),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book Page'),
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['bid'] = [
|
||||
'title' => t('Top level book'),
|
||||
'help' => t('The book the node is in.'),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book'),
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['pid'] = [
|
||||
'title' => t('Parent'),
|
||||
'help' => t('The parent book node.'),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book Parent'),
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['has_children'] = [
|
||||
'title' => t('Page has Children'),
|
||||
'help' => t('Flag indicating whether this book page has children'),
|
||||
'field' => [
|
||||
'id' => 'boolean',
|
||||
],
|
||||
'sort' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
'filter' => [
|
||||
'id' => 'boolean',
|
||||
'label' => t('Has Children'),
|
||||
],
|
||||
'argument' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['weight'] = [
|
||||
'title' => t('Weight'),
|
||||
'help' => t('The weight of the book page.'),
|
||||
'field' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
'sort' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
];
|
||||
|
||||
$data['book']['depth'] = [
|
||||
'title' => t('Depth'),
|
||||
'help' => t('The depth of the book page in the hierarchy; top level books have a depth of 1.'),
|
||||
'field' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
'sort' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
'filter' => [
|
||||
'id' => 'numeric',
|
||||
],
|
||||
'argument' => [
|
||||
'id' => 'standard',
|
||||
],
|
||||
];
|
||||
$parents = [
|
||||
1 => t('1st parent'),
|
||||
2 => t('2nd parent'),
|
||||
3 => t('3rd parent'),
|
||||
4 => t('4th parent'),
|
||||
5 => t('5th parent'),
|
||||
6 => t('6th parent'),
|
||||
7 => t('7th parent'),
|
||||
8 => t('8th parent'),
|
||||
9 => t('9th parent'),
|
||||
];
|
||||
foreach ($parents as $i => $parent_label) {
|
||||
$data['book']["p$i"] = [
|
||||
'title' => $parent_label,
|
||||
'help' => t('The @parent of book node.', ['@parent' => $parent_label]),
|
||||
'relationship' => [
|
||||
'base' => 'node_field_data',
|
||||
'id' => 'standard',
|
||||
'label' => t('Book @parent', ['@parent' => $parent_label]),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
|
@ -8,6 +8,6 @@ name: 'Book page'
|
|||
type: book
|
||||
description: '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.'
|
||||
help: ''
|
||||
new_revision: false
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
display_submitted: true
|
||||
|
|
8
core/modules/book/config/schema/book.views.schema.yml
Normal file
8
core/modules/book/config/schema/book.views.schema.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Schema for the views plugins of the Book module.
|
||||
|
||||
views.argument_default.top_level_book:
|
||||
type: sequence
|
||||
label: 'Top Level Book from current node'
|
||||
sequence:
|
||||
type: string
|
||||
label: 'Nid'
|
|
@ -138,7 +138,7 @@ class BookManager implements BookManagerInterface {
|
|||
/**
|
||||
* Determine the relative depth of the children of a given book link.
|
||||
*
|
||||
* @param array
|
||||
* @param array $book_link
|
||||
* The book link.
|
||||
*
|
||||
* @return int
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Drupal\book\Cache;
|
|||
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\Context\CacheContextInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +20,9 @@ use Symfony\Component\HttpFoundation\RequestStack;
|
|||
* This class is container-aware to avoid initializing the 'book.manager'
|
||||
* service when it is not necessary.
|
||||
*/
|
||||
class BookNavigationCacheContext extends ContainerAware implements CacheContextInterface {
|
||||
class BookNavigationCacheContext implements CacheContextInterface, ContainerAwareInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\book\Plugin\views\argument_default;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\node\NodeStorageInterface;
|
||||
use Drupal\node\Plugin\views\argument_default\Node;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Default argument plugin to get the current node's top level book.
|
||||
*
|
||||
* @ViewsArgumentDefault(
|
||||
* id = "top_level_book",
|
||||
* title = @Translation("Top Level Book from current node")
|
||||
* )
|
||||
*/
|
||||
class TopLevelBook extends Node {
|
||||
|
||||
/**
|
||||
* The node storage controller.
|
||||
*
|
||||
* @var \Drupal\node\NodeStorageInterface
|
||||
*/
|
||||
protected $nodeStorage;
|
||||
|
||||
/**
|
||||
* Constructs a Drupal\book\Plugin\views\argument_default\TopLevelBook object.
|
||||
*
|
||||
* @param array $configuration
|
||||
* A configuration array containing information about the plugin instance.
|
||||
* @param string $plugin_id
|
||||
* The plugin_id for the plugin instance.
|
||||
* @param array $plugin_definition
|
||||
* The plugin implementation definition.
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
* @param \Drupal\node\NodeStorageInterface $node_storage
|
||||
* The node storage controller.
|
||||
*/
|
||||
public function __construct(array $configuration, $plugin_id, array $plugin_definition, RouteMatchInterface $route_match, NodeStorageInterface $node_storage) {
|
||||
parent::__construct($configuration, $plugin_id, $plugin_definition, $route_match);
|
||||
$this->nodeStorage = $node_storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
|
||||
return new static(
|
||||
$configuration,
|
||||
$plugin_id,
|
||||
$plugin_definition,
|
||||
$container->get('current_route_match'),
|
||||
$container->get('entity.manager')->getStorage('node')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getArgument() {
|
||||
// Use the argument_default_node plugin to get the nid argument.
|
||||
$nid = parent::getArgument();
|
||||
if (!empty($nid)) {
|
||||
$node = $this->nodeStorage->load($nid);
|
||||
if (isset($node->book['bid'])) {
|
||||
return $node->book['bid'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -526,49 +526,49 @@ class BookTest extends WebTestBase {
|
|||
/**
|
||||
* Tests the access for deleting top-level book nodes.
|
||||
*/
|
||||
function testBookDelete() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array();
|
||||
function testBookDelete() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$edit = array();
|
||||
|
||||
// Test access to delete top-level and child book nodes.
|
||||
$this->drupalGet('node/' . $this->book->id() . '/outline/remove');
|
||||
$this->assertResponse('403', 'Deleting top-level book node properly forbidden.');
|
||||
$this->drupalPostForm('node/' . $nodes[4]->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($nodes[4]->id()));
|
||||
$node4 = $node_storage->load($nodes[4]->id());
|
||||
$this->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
|
||||
// Test access to delete top-level and child book nodes.
|
||||
$this->drupalGet('node/' . $this->book->id() . '/outline/remove');
|
||||
$this->assertResponse('403', 'Deleting top-level book node properly forbidden.');
|
||||
$this->drupalPostForm('node/' . $nodes[4]->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($nodes[4]->id()));
|
||||
$node4 = $node_storage->load($nodes[4]->id());
|
||||
$this->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
|
||||
|
||||
// Delete all child book nodes and retest top-level node deletion.
|
||||
foreach ($nodes as $node) {
|
||||
$nids[] = $node->id();
|
||||
}
|
||||
entity_delete_multiple('node', $nids);
|
||||
$this->drupalPostForm('node/' . $this->book->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($this->book->id()));
|
||||
$node = $node_storage->load($this->book->id());
|
||||
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
|
||||
// Delete all child book nodes and retest top-level node deletion.
|
||||
foreach ($nodes as $node) {
|
||||
$nids[] = $node->id();
|
||||
}
|
||||
entity_delete_multiple('node', $nids);
|
||||
$this->drupalPostForm('node/' . $this->book->id() . '/outline/remove', $edit, t('Remove'));
|
||||
$node_storage->resetCache(array($this->book->id()));
|
||||
$node = $node_storage->load($this->book->id());
|
||||
$this->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
|
||||
|
||||
// Tests directly deleting a book parent.
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet($this->book->urlInfo('delete-form'));
|
||||
$this->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $this->book->label()]));
|
||||
// Delete parent, and visit a child page.
|
||||
$this->drupalPostForm($this->book->urlInfo('delete-form'), [], t('Delete'));
|
||||
$this->drupalGet($nodes[0]->urlInfo());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($nodes[0]->label());
|
||||
// The book parents should be updated.
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
$child = $node_storage->load($nodes[0]->id());
|
||||
$this->assertEqual($child->id(), $child->book['bid'], 'Child node book ID updated when parent is deleted.');
|
||||
// 3rd-level children should now be 2nd-level.
|
||||
$second = $node_storage->load($nodes[1]->id());
|
||||
$this->assertEqual($child->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
|
||||
}
|
||||
// Tests directly deleting a book parent.
|
||||
$nodes = $this->createBook();
|
||||
$this->drupalLogin($this->adminUser);
|
||||
$this->drupalGet($this->book->urlInfo('delete-form'));
|
||||
$this->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', ['%title' => $this->book->label()]));
|
||||
// Delete parent, and visit a child page.
|
||||
$this->drupalPostForm($this->book->urlInfo('delete-form'), [], t('Delete'));
|
||||
$this->drupalGet($nodes[0]->urlInfo());
|
||||
$this->assertResponse(200);
|
||||
$this->assertText($nodes[0]->label());
|
||||
// The book parents should be updated.
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
$child = $node_storage->load($nodes[0]->id());
|
||||
$this->assertEqual($child->id(), $child->book['bid'], 'Child node book ID updated when parent is deleted.');
|
||||
// 3rd-level children should now be 2nd-level.
|
||||
$second = $node_storage->load($nodes[1]->id());
|
||||
$this->assertEqual($child->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests re-ordering of books.
|
||||
|
|
156
core/modules/book/src/Tests/Views/BookRelationshipTest.php
Normal file
156
core/modules/book/src/Tests/Views/BookRelationshipTest.php
Normal file
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\book\Tests\Views;
|
||||
|
||||
use Drupal\views\Tests\ViewTestBase;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Tests entity reference relationship data.
|
||||
*
|
||||
* @group book
|
||||
*
|
||||
* @see book_views_data()
|
||||
*/
|
||||
class BookRelationshipTest extends ViewTestBase {
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = array('test_book_view');
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('book_test_views', 'book', 'views');
|
||||
|
||||
/**
|
||||
* A book node.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $book;
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $bookAuthor;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Create users.
|
||||
$this->bookAuthor = $this->drupalCreateUser(
|
||||
array(
|
||||
'create new books',
|
||||
'create book content',
|
||||
'edit own book content',
|
||||
'add content to books',
|
||||
)
|
||||
);
|
||||
ViewTestData::createTestViews(get_class($this), array('book_test_views'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new book with a page hierarchy.
|
||||
*/
|
||||
protected function createBook() {
|
||||
// Create new book.
|
||||
$this->drupalLogin($this->bookAuthor);
|
||||
|
||||
$this->book = $this->createBookNode('new');
|
||||
$book = $this->book;
|
||||
|
||||
$nodes = array();
|
||||
// Node 0.
|
||||
$nodes[] = $this->createBookNode($book->id());
|
||||
// Node 1.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid']);
|
||||
// Node 2.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[1]->book['nid']);
|
||||
// Node 3.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[2]->book['nid']);
|
||||
// Node 4.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[3]->book['nid']);
|
||||
// Node 5.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[4]->book['nid']);
|
||||
// Node 6.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[5]->book['nid']);
|
||||
// Node 7.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[6]->book['nid']);
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a book node.
|
||||
*
|
||||
* @param int|string $book_nid
|
||||
* A book node ID or set to 'new' to create a new book.
|
||||
* @param int|null $parent
|
||||
* (optional) Parent book reference ID. Defaults to NULL.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The book node.
|
||||
*/
|
||||
protected function createBookNode($book_nid, $parent = NULL) {
|
||||
// $number does not use drupal_static as it should not be reset
|
||||
// since it uniquely identifies each call to createBookNode().
|
||||
// Used to ensure that when sorted nodes stay in same order.
|
||||
static $number = 0;
|
||||
|
||||
$edit = array();
|
||||
$edit['title[0][value]'] = $number . ' - SimpleTest test node ' . $this->randomMachineName(10);
|
||||
$edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomMachineName(32) . ' ' . $this->randomMachineName(32);
|
||||
$edit['book[bid]'] = $book_nid;
|
||||
|
||||
if ($parent !== NULL) {
|
||||
$this->drupalPostForm('node/add/book', $edit, t('Change book (update list of parents)'));
|
||||
|
||||
$edit['book[pid]'] = $parent;
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
// Make sure the parent was flagged as having children.
|
||||
$parent_node = \Drupal::entityManager()->getStorage('node')->loadUnchanged($parent);
|
||||
$this->assertFalse(empty($parent_node->book['has_children']), 'Parent node is marked as having children');
|
||||
}
|
||||
else {
|
||||
$this->drupalPostForm('node/add/book', $edit, t('Save'));
|
||||
}
|
||||
|
||||
// Check to make sure the book node was created.
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.');
|
||||
$number++;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests using the views relationship.
|
||||
*/
|
||||
public function testRelationship() {
|
||||
|
||||
// Create new book.
|
||||
// @var \Drupal\node\NodeInterface[] $nodes
|
||||
$nodes = $this->createBook();
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$this->drupalGet('test-book/' . $nodes[$i]->id());
|
||||
|
||||
for ($j = 0; $j < $i; $j++) {
|
||||
$this->assertLink($nodes[$j]->label());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
name: 'Book test views'
|
||||
type: module
|
||||
description: 'Provides default views for views book tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
core: 8.x
|
||||
dependencies:
|
||||
- book
|
||||
- views
|
File diff suppressed because it is too large
Load diff
Reference in a new issue