upgrades core to 8.4.2
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @file
|
||||
* Javascript behaviors for the Book module.
|
||||
*/
|
||||
|
||||
(function ($, Drupal) {
|
||||
/**
|
||||
* Adds summaries to the book outline form.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches summary behavior to book outline forms.
|
||||
*/
|
||||
Drupal.behaviors.bookDetailsSummaries = {
|
||||
attach(context) {
|
||||
$(context).find('.book-outline-form').drupalSetSummary((context) => {
|
||||
const $select = $(context).find('.book-title-select');
|
||||
const val = $select.val();
|
||||
|
||||
if (val === '0') {
|
||||
return Drupal.t('Not in book');
|
||||
}
|
||||
else if (val === 'new') {
|
||||
return Drupal.t('New book');
|
||||
}
|
||||
|
||||
return Drupal.checkPlain($select.find(':selected').text());
|
||||
});
|
||||
},
|
||||
};
|
||||
}(jQuery, Drupal));
|
||||
@@ -8,8 +8,8 @@ dependencies:
|
||||
- node
|
||||
configure: book.settings
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
+10
-22
@@ -1,37 +1,25 @@
|
||||
/**
|
||||
* @file
|
||||
* Javascript behaviors for the Book module.
|
||||
*/
|
||||
* DO NOT EDIT THIS FILE.
|
||||
* See the following change record for more information,
|
||||
* https://www.drupal.org/node/2815083
|
||||
* @preserve
|
||||
**/
|
||||
|
||||
(function ($, Drupal) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Adds summaries to the book outline form.
|
||||
*
|
||||
* @type {Drupal~behavior}
|
||||
*
|
||||
* @prop {Drupal~behaviorAttach} attach
|
||||
* Attaches summary behavior to book outline forms.
|
||||
*/
|
||||
Drupal.behaviors.bookDetailsSummaries = {
|
||||
attach: function (context) {
|
||||
attach: function attach(context) {
|
||||
$(context).find('.book-outline-form').drupalSetSummary(function (context) {
|
||||
var $select = $(context).find('.book-title-select');
|
||||
var val = $select.val();
|
||||
|
||||
if (val === '0') {
|
||||
return Drupal.t('Not in book');
|
||||
}
|
||||
else if (val === 'new') {
|
||||
} else if (val === 'new') {
|
||||
return Drupal.t('New book');
|
||||
}
|
||||
else {
|
||||
return Drupal.checkPlain($select.find(':selected').text());
|
||||
}
|
||||
|
||||
return Drupal.checkPlain($select.find(':selected').text());
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
})(jQuery, Drupal);
|
||||
})(jQuery, Drupal);
|
||||
@@ -82,7 +82,8 @@ function book_entity_type_build(array &$entity_types) {
|
||||
$entity_types['node']
|
||||
->setFormClass('book_outline', 'Drupal\book\Form\BookOutlineForm')
|
||||
->setLinkTemplate('book-outline-form', '/node/{node}/outline')
|
||||
->setLinkTemplate('book-remove-form', '/node/{node}/outline/remove');
|
||||
->setLinkTemplate('book-remove-form', '/node/{node}/outline/remove')
|
||||
->addConstraint('BookOutline', []);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,7 +438,7 @@ function template_preprocess_book_navigation(&$variables) {
|
||||
}
|
||||
|
||||
if (!empty($build)) {
|
||||
drupal_render($build);
|
||||
\Drupal::service('renderer')->render($build);
|
||||
}
|
||||
|
||||
$variables['has_links'] = FALSE;
|
||||
|
||||
@@ -33,6 +33,13 @@ content:
|
||||
weight: 15
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
status:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
display_label: true
|
||||
weight: 120
|
||||
region: content
|
||||
third_party_settings: { }
|
||||
sticky:
|
||||
type: boolean_checkbox
|
||||
settings:
|
||||
|
||||
@@ -8,6 +8,7 @@ source:
|
||||
- book_child_type
|
||||
- book_block_mode
|
||||
- book_allowed_types
|
||||
source_module: book
|
||||
process:
|
||||
child_type: book_child_type
|
||||
'block/navigation/mode': book_block_mode
|
||||
|
||||
@@ -275,6 +275,20 @@ class BookManager implements BookManagerInterface {
|
||||
// handle it here if it did not.
|
||||
$node->book['pid'] = $node->book['bid'];
|
||||
}
|
||||
|
||||
// Prevent changes to the book outline if the node being saved is not the
|
||||
// default revision.
|
||||
$updated = FALSE;
|
||||
if (!$new) {
|
||||
$original = $this->loadBookLink($node->id(), FALSE);
|
||||
if ($node->book['bid'] != $original['bid'] || $node->book['pid'] != $original['pid'] || $node->book['weight'] != $original['weight']) {
|
||||
$updated = TRUE;
|
||||
}
|
||||
}
|
||||
if (($new || $updated) && !$node->isDefaultRevision()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $this->saveBookLink($node->book, $new);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\node\NodeInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Provides an interface defining a book manager.
|
||||
*/
|
||||
|
||||
@@ -220,7 +220,7 @@ class BookAdminEditForm extends FormBase {
|
||||
}
|
||||
|
||||
$form[$id]['title'] = [
|
||||
'#prefix' => !empty($indentation) ? drupal_render($indentation) : '',
|
||||
'#prefix' => !empty($indentation) ? \Drupal::service('renderer')->render($indentation) : '',
|
||||
'#type' => 'textfield',
|
||||
'#default_value' => $data['link']['title'],
|
||||
'#maxlength' => 255,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\book\Plugin\Validation\Constraint;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* Validation constraint for changing the book outline in pending revisions.
|
||||
*
|
||||
* @Constraint(
|
||||
* id = "BookOutline",
|
||||
* label = @Translation("Book outline.", context = "Validation"),
|
||||
* )
|
||||
*/
|
||||
class BookOutlineConstraint extends Constraint {
|
||||
|
||||
public $message = 'You can only change the book outline for the <em>published</em> version of this content.';
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\book\Plugin\Validation\Constraint;
|
||||
|
||||
use Drupal\book\BookManagerInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Constraint validator for changing the book outline in pending revisions.
|
||||
*/
|
||||
class BookOutlineConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
|
||||
|
||||
/**
|
||||
* The book manager.
|
||||
*
|
||||
* @var \Drupal\book\BookManagerInterface
|
||||
*/
|
||||
protected $bookManager;
|
||||
|
||||
/**
|
||||
* Creates a new BookOutlineConstraintValidator instance.
|
||||
*
|
||||
* @param \Drupal\book\BookManagerInterface $book_manager
|
||||
* The book manager.
|
||||
*/
|
||||
public function __construct(BookManagerInterface $book_manager) {
|
||||
$this->bookManager = $book_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('book.manager')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validate($entity, Constraint $constraint) {
|
||||
if (isset($entity) && !$entity->isNew() && !$entity->isDefaultRevision()) {
|
||||
/** @var \Drupal\Core\Entity\ContentEntityInterface $original */
|
||||
$original = $this->bookManager->loadBookLink($entity->id(), FALSE) ?: [
|
||||
'bid' => 0,
|
||||
'weight' => 0,
|
||||
];
|
||||
if (empty($original['pid'])) {
|
||||
$original['pid'] = -1;
|
||||
}
|
||||
|
||||
if ($entity->book['bid'] != $original['bid']) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->atPath('book.bid')
|
||||
->setInvalidValue($entity)
|
||||
->addViolation();
|
||||
}
|
||||
if ($entity->book['pid'] != $original['pid']) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->atPath('book.pid')
|
||||
->setInvalidValue($entity)
|
||||
->addViolation();
|
||||
}
|
||||
if ($entity->book['weight'] != $original['weight']) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->atPath('book.weight')
|
||||
->setInvalidValue($entity)
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,7 +8,8 @@ use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
|
||||
* Drupal 6 book source.
|
||||
*
|
||||
* @MigrateSource(
|
||||
* id = "d6_book"
|
||||
* id = "d6_book",
|
||||
* source_module = "book"
|
||||
* )
|
||||
*/
|
||||
class Book extends DrupalSqlBase {
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -5,8 +5,8 @@ package: Testing
|
||||
# version: VERSION
|
||||
# core: 8.x
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -8,8 +8,8 @@ dependencies:
|
||||
- book
|
||||
- views
|
||||
|
||||
# Information added by Drupal.org packaging script on 2017-08-16
|
||||
version: '8.3.7'
|
||||
# Information added by Drupal.org packaging script on 2017-11-03
|
||||
version: '8.4.2'
|
||||
core: '8.x'
|
||||
project: 'drupal'
|
||||
datestamp: 1502903957
|
||||
datestamp: 1509719929
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\book\Functional;
|
||||
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\workflows\Entity\Workflow;
|
||||
|
||||
/**
|
||||
* Tests Book and Content Moderation integration.
|
||||
*
|
||||
* @group book
|
||||
*/
|
||||
class BookContentModerationTest extends BrowserTestBase {
|
||||
|
||||
use BookTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['book', 'block', 'book_test', 'content_moderation'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
|
||||
$workflow = Workflow::load('editorial');
|
||||
$workflow->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 testBookWithPendingRevisions() {
|
||||
// Create two books.
|
||||
$book_1_nodes = $this->createBook(['moderation_state[0][state]' => 'published']);
|
||||
$book_1 = $this->book;
|
||||
|
||||
$this->createBook(['moderation_state[0][state]' => 'published']);
|
||||
$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]);
|
||||
|
||||
// Create a new book page without actually attaching it to a book and create
|
||||
// a draft.
|
||||
$edit = [
|
||||
'title[0][value]' => $this->randomString(),
|
||||
'moderation_state[0][state]' => 'published',
|
||||
];
|
||||
$this->drupalPostForm('node/add/book', $edit, t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
|
||||
$this->assertTrue($node);
|
||||
|
||||
$edit = [
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.');
|
||||
|
||||
// Create a book draft with no changes, then publish it.
|
||||
$edit = [
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $book_1->id() . '/edit', $edit, t('Save'));
|
||||
$this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.');
|
||||
$edit = [
|
||||
'moderation_state[0][state]' => 'published',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $book_1->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Try to move Node 2 to a different parent.
|
||||
$edit = [
|
||||
'book[pid]' => $book_1_nodes[3]->id(),
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->assertSession()->pageTextContains('You can only change the book outline for the published version of this content.');
|
||||
|
||||
// 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(),
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->assertSession()->pageTextContains('You can only change the book outline for the published version of this content.');
|
||||
|
||||
// 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 change the weight of Node 2.
|
||||
$edit = [
|
||||
'book[weight]' => 2,
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->assertSession()->pageTextContains('You can only change the book outline for the published version of this content.');
|
||||
|
||||
// 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]);
|
||||
|
||||
// Save a new draft revision for the node without any changes and check that
|
||||
// the error message is not displayed.
|
||||
$edit = [
|
||||
'moderation_state[0][state]' => 'draft',
|
||||
];
|
||||
$this->drupalPostForm('node/' . $book_1_nodes[1]->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
$this->assertSession()->pageTextNotContains('You can only change the book outline for the published version of this content.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
namespace Drupal\Tests\book\Functional;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Tests\BrowserTestBase;
|
||||
use Drupal\user\RoleInterface;
|
||||
|
||||
@@ -15,6 +13,8 @@ use Drupal\user\RoleInterface;
|
||||
*/
|
||||
class BookTest extends BrowserTestBase {
|
||||
|
||||
use BookTestTrait;
|
||||
|
||||
/**
|
||||
* Modules to install.
|
||||
*
|
||||
@@ -22,20 +22,6 @@ class BookTest extends BrowserTestBase {
|
||||
*/
|
||||
public static $modules = ['book', 'block', 'node_access_test', 'book_test'];
|
||||
|
||||
/**
|
||||
* A book node.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $book;
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $bookAuthor;
|
||||
|
||||
/**
|
||||
* A user with permission to view a book and access printer-friendly version.
|
||||
*
|
||||
@@ -75,39 +61,6 @@ class BookTest extends BrowserTestBase {
|
||||
$this->adminUser = $this->drupalCreateUser(['create new books', 'create book content', 'edit any book content', 'delete any 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.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
public 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 = [];
|
||||
$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 the book navigation cache context.
|
||||
*
|
||||
@@ -199,7 +152,8 @@ class BookTest extends BrowserTestBase {
|
||||
* |- Node 5
|
||||
* |- Node 4
|
||||
*/
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[3]->book['nid']); // Node 5.
|
||||
// Node 5.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[3]->book['nid']);
|
||||
$this->drupalLogout();
|
||||
$this->drupalLogin($this->webUser);
|
||||
// Verify the new outline - make sure we don't get stale cached data.
|
||||
@@ -229,147 +183,6 @@ class BookTest extends BrowserTestBase {
|
||||
$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.
|
||||
*/
|
||||
public 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.', ['@number' => $number]));
|
||||
}
|
||||
else {
|
||||
$this->pass(format_string('Node %number does not have outline.', ['%number' => $number]));
|
||||
}
|
||||
|
||||
// Check previous, up, and next links.
|
||||
if ($previous) {
|
||||
/** @var \Drupal\Core\Url $url */
|
||||
$url = $previous->urlInfo();
|
||||
$url->setOptions(['attributes' => ['rel' => ['prev'], 'title' => t('Go to previous page')]]);
|
||||
$text = SafeMarkup::format('<b>‹</b> @label', ['@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(['attributes' => ['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(['attributes' => ['rel' => ['next'], 'title' => t('Go to next page')]]);
|
||||
$text = SafeMarkup::format('@label <b>›</b>', ['@label' => $next->label()]);
|
||||
$this->assertRaw(\Drupal::l($text, $url), 'Next page link found.');
|
||||
}
|
||||
|
||||
// Compute the expected breadcrumb.
|
||||
$expected_breadcrumb = [];
|
||||
$expected_breadcrumb[] = \Drupal::url('<front>');
|
||||
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 = [];
|
||||
foreach ($links as $link) {
|
||||
$got_breadcrumb[] = $link->getAttribute('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.
|
||||
*/
|
||||
public function generateOutlinePattern($nodes) {
|
||||
$outline = '';
|
||||
foreach ($nodes as $node) {
|
||||
$outline .= '(node\/' . $node->id() . ')(.*?)(' . $node->label() . ')(.*?)';
|
||||
}
|
||||
|
||||
return '/<nav id="book-navigation-' . $this->book->id() . '"(.*?)<ul(.*?)' . $outline . '<\/ul>/s';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 created node.
|
||||
*/
|
||||
public 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().
|
||||
static $number = 0; // Used to ensure that when sorted nodes stay in same order.
|
||||
|
||||
$edit = [];
|
||||
$edit['title[0][value]'] = str_pad($number, 2, '0', STR_PAD_LEFT) . ' - 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 book export ("printer-friendly version") functionality.
|
||||
*/
|
||||
@@ -741,8 +554,8 @@ class BookTest extends BrowserTestBase {
|
||||
$this->drupalPlaceBlock('book_navigation', ['block_mode' => 'book pages']);
|
||||
|
||||
// Unpublish book node.
|
||||
$edit = [];
|
||||
$this->drupalPostForm('node/' . $this->book->id() . '/edit', $edit, t('Save and unpublish'));
|
||||
$edit = ['status[value]' => FALSE];
|
||||
$this->drupalPostForm('node/' . $this->book->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Test node page.
|
||||
$this->drupalGet('node/' . $this->book->id());
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\book\Functional;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
|
||||
/**
|
||||
* Provides common functionality for Book test classes.
|
||||
*/
|
||||
trait BookTestTrait {
|
||||
|
||||
/**
|
||||
* A book node.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface
|
||||
*/
|
||||
protected $book;
|
||||
|
||||
/**
|
||||
* A user with permission to create and edit books.
|
||||
*
|
||||
* @var \Drupal\Core\Session\AccountInterface
|
||||
*/
|
||||
protected $bookAuthor;
|
||||
|
||||
/**
|
||||
* Creates a new book with a page hierarchy.
|
||||
*
|
||||
* @param array $edit
|
||||
* (optional) Field data in an associative array. Changes the current input
|
||||
* fields (where possible) to the values indicated. Defaults to an empty
|
||||
* array.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
public function createBook($edit = []) {
|
||||
// Create new book.
|
||||
$this->drupalLogin($this->bookAuthor);
|
||||
|
||||
$this->book = $this->createBookNode('new', NULL, $edit);
|
||||
$book = $this->book;
|
||||
|
||||
/*
|
||||
* Add page hierarchy to book.
|
||||
* Book
|
||||
* |- Node 0
|
||||
* |- Node 1
|
||||
* |- Node 2
|
||||
* |- Node 3
|
||||
* |- Node 4
|
||||
*/
|
||||
$nodes = [];
|
||||
// Node 0.
|
||||
$nodes[] = $this->createBookNode($book->id(), NULL, $edit);
|
||||
// Node 1.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $edit);
|
||||
// Node 2.
|
||||
$nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $edit);
|
||||
// Node 3.
|
||||
$nodes[] = $this->createBookNode($book->id(), NULL, $edit);
|
||||
// Node 4.
|
||||
$nodes[] = $this->createBookNode($book->id(), NULL, $edit);
|
||||
|
||||
$this->drupalLogout();
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Previous link node.
|
||||
* @param $up
|
||||
* Up link node.
|
||||
* @param $next
|
||||
* Next link node.
|
||||
* @param array $breadcrumb
|
||||
* The nodes that should be displayed in the breadcrumb.
|
||||
*/
|
||||
public function checkBookNode(EntityInterface $node, $nodes, $previous, $up, $next, 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.', ['@number' => $number]));
|
||||
}
|
||||
else {
|
||||
$this->pass(format_string('Node %number does not have outline.', ['%number' => $number]));
|
||||
}
|
||||
|
||||
// Check previous, up, and next links.
|
||||
if ($previous) {
|
||||
/** @var \Drupal\Core\Url $url */
|
||||
$url = $previous->urlInfo();
|
||||
$url->setOptions(['attributes' => ['rel' => ['prev'], 'title' => t('Go to previous page')]]);
|
||||
$text = SafeMarkup::format('<b>‹</b> @label', ['@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(['attributes' => ['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(['attributes' => ['rel' => ['next'], 'title' => t('Go to next page')]]);
|
||||
$text = SafeMarkup::format('@label <b>›</b>', ['@label' => $next->label()]);
|
||||
$this->assertRaw(\Drupal::l($text, $url), 'Next page link found.');
|
||||
}
|
||||
|
||||
// Compute the expected breadcrumb.
|
||||
$expected_breadcrumb = [];
|
||||
$expected_breadcrumb[] = \Drupal::url('<front>');
|
||||
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 = [];
|
||||
foreach ($links as $link) {
|
||||
$got_breadcrumb[] = $link->getAttribute('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.
|
||||
*/
|
||||
public function generateOutlinePattern($nodes) {
|
||||
$outline = '';
|
||||
foreach ($nodes as $node) {
|
||||
$outline .= '(node\/' . $node->id() . ')(.*?)(' . $node->label() . ')(.*?)';
|
||||
}
|
||||
|
||||
return '/<nav id="book-navigation-' . $this->book->id() . '"(.*?)<ul(.*?)' . $outline . '<\/ul>/s';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param array $edit
|
||||
* (optional) Field data in an associative array. Changes the current input
|
||||
* fields (where possible) to the values indicated. Defaults to an empty
|
||||
* array.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The created node.
|
||||
*/
|
||||
public function createBookNode($book_nid, $parent = NULL, $edit = []) {
|
||||
// $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['title[0][value]'] = str_pad($number, 2, '0', STR_PAD_LEFT) . ' - 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\Tests\book\Kernel;
|
||||
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the Book module handles pending revisions correctly.
|
||||
*
|
||||
* @group book
|
||||
*/
|
||||
class BookPendingRevisionTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['system', 'user', 'field', 'filter', 'text', 'node', 'book'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('user');
|
||||
$this->installEntitySchema('node');
|
||||
$this->installSchema('book', ['book']);
|
||||
$this->installSchema('node', ['node_access']);
|
||||
$this->installConfig(['node', 'book', 'field']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests pending revision handling for books.
|
||||
*/
|
||||
public function testBookWithPendingRevisions() {
|
||||
$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_1_child = Node::create(['title' => $this->randomString(), 'type' => $content_type->id()]);
|
||||
$book_1_child->book['bid'] = $book_1->id();
|
||||
$book_1_child->book['pid'] = $book_1->id();
|
||||
$book_1_child->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 pending
|
||||
// revision.
|
||||
/** @var \Drupal\book\BookManagerInterface $book_manager */
|
||||
$book_manager = $this->container->get('book.manager');
|
||||
|
||||
// Check that the API doesn't allow us to change the book outline for
|
||||
// pending revisions.
|
||||
$child->book['bid'] = $book_2->id();
|
||||
$child->setNewRevision(TRUE);
|
||||
$child->isDefaultRevision(FALSE);
|
||||
|
||||
$this->assertFalse($book_manager->updateOutline($child), 'A pending revision can not change the book outline.');
|
||||
|
||||
// Check that the API doesn't allow us to change the book parent for
|
||||
// pending revisions.
|
||||
$child = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($child->id());
|
||||
$child->book['pid'] = $book_1_child->id();
|
||||
$child->setNewRevision(TRUE);
|
||||
$child->isDefaultRevision(FALSE);
|
||||
|
||||
$this->assertFalse($book_manager->updateOutline($child), 'A pending revision can not change the book outline.');
|
||||
|
||||
// Check that the API doesn't allow us to change the book weight for
|
||||
// pending revisions.
|
||||
$child = \Drupal::entityTypeManager()->getStorage('node')->loadUnchanged($child->id());
|
||||
$child->book['weight'] = 2;
|
||||
$child->setNewRevision(TRUE);
|
||||
$child->isDefaultRevision(FALSE);
|
||||
|
||||
$this->assertFalse($book_manager->updateOutline($child), 'A pending revision can not change the book outline.');
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user