forgot some core files
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\node\Tests;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\node\Entity\Node;
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
|
||||
/**
|
||||
* Create a node with revisions and test viewing, saving, reverting, and
|
||||
* deleting revisions for users with access for this content type.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeRevisionsTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* An array of node revisions.
|
||||
*
|
||||
* @var \Drupal\node\NodeInterface[]
|
||||
*/
|
||||
protected $nodes;
|
||||
|
||||
/**
|
||||
* Revision log messages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $revisionLogs;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['node', 'contextual', 'datetime', 'language', 'content_translation'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Enable additional languages.
|
||||
ConfigurableLanguage::createFromLangcode('de')->save();
|
||||
ConfigurableLanguage::createFromLangcode('it')->save();
|
||||
|
||||
$field_storage_definition = [
|
||||
'field_name' => 'untranslatable_string_field',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'string',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
];
|
||||
$field_storage = FieldStorageConfig::create($field_storage_definition);
|
||||
$field_storage->save();
|
||||
|
||||
$field_definition = [
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
];
|
||||
$field = FieldConfig::create($field_definition);
|
||||
$field->save();
|
||||
|
||||
// Enable translation for page nodes.
|
||||
\Drupal::service('content_translation.manager')->setEnabled('node', 'page', TRUE);
|
||||
|
||||
// Create and log in user.
|
||||
$web_user = $this->drupalCreateUser(
|
||||
[
|
||||
'view page revisions',
|
||||
'revert page revisions',
|
||||
'delete page revisions',
|
||||
'edit any page content',
|
||||
'delete any page content',
|
||||
'access contextual links',
|
||||
'translate any entity',
|
||||
'administer content types',
|
||||
]
|
||||
);
|
||||
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Create initial node.
|
||||
$node = $this->drupalCreateNode();
|
||||
$settings = get_object_vars($node);
|
||||
$settings['revision'] = 1;
|
||||
$settings['isDefaultRevision'] = TRUE;
|
||||
|
||||
$nodes = [];
|
||||
$logs = [];
|
||||
|
||||
// Get original node.
|
||||
$nodes[] = clone $node;
|
||||
|
||||
// Create three revisions.
|
||||
$revision_count = 3;
|
||||
for ($i = 0; $i < $revision_count; $i++) {
|
||||
$logs[] = $node->revision_log = $this->randomMachineName(32);
|
||||
|
||||
// Create revision with a random title and body and update variables.
|
||||
$node->title = $this->randomMachineName();
|
||||
$node->body = [
|
||||
'value' => $this->randomMachineName(32),
|
||||
'format' => filter_default_format(),
|
||||
];
|
||||
$node->untranslatable_string_field->value = $this->randomString();
|
||||
$node->setNewRevision();
|
||||
|
||||
// Edit the 1st and 2nd revision with a different user.
|
||||
if ($i < 2) {
|
||||
$editor = $this->drupalCreateUser();
|
||||
$node->setRevisionUserId($editor->id());
|
||||
}
|
||||
else {
|
||||
$node->setRevisionUserId($web_user->id());
|
||||
}
|
||||
|
||||
$node->save();
|
||||
|
||||
// Make sure we get revision information.
|
||||
$node = Node::load($node->id());
|
||||
$nodes[] = clone $node;
|
||||
}
|
||||
|
||||
$this->nodes = $nodes;
|
||||
$this->revisionLogs = $logs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks node revision related operations.
|
||||
*/
|
||||
public function testRevisions() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$nodes = $this->nodes;
|
||||
$logs = $this->revisionLogs;
|
||||
|
||||
// Get last node for simple checks.
|
||||
$node = $nodes[3];
|
||||
|
||||
// Confirm the correct revision text appears on "view revisions" page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions/" . $node->getRevisionId() . "/view");
|
||||
$this->assertText($node->body->value, 'Correct text displays for version.');
|
||||
|
||||
// Confirm the correct log message appears on "revisions overview" page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
foreach ($logs as $revision_log) {
|
||||
$this->assertText($revision_log, 'Revision log message found.');
|
||||
}
|
||||
// Original author, and editor names should appear on revisions overview.
|
||||
$web_user = $nodes[0]->revision_uid->entity;
|
||||
$this->assertText(t('by @name', ['@name' => $web_user->getAccountName()]));
|
||||
$editor = $nodes[2]->revision_uid->entity;
|
||||
$this->assertText(t('by @name', ['@name' => $editor->getAccountName()]));
|
||||
|
||||
// Confirm that this is the default revision.
|
||||
$this->assertTrue($node->isDefaultRevision(), 'Third node revision is the default one.');
|
||||
|
||||
// Confirm that the "Edit" and "Delete" contextual links appear for the
|
||||
// default revision.
|
||||
$ids = ['node:node=' . $node->id() . ':changed=' . $node->getChangedTime()];
|
||||
$json = $this->renderContextualLinks($ids, 'node/' . $node->id());
|
||||
$this->verbose($json[$ids[0]]);
|
||||
|
||||
$expected = '<li class="entitynodeedit-form"><a href="' . base_path() . 'node/' . $node->id() . '/edit">Edit</a></li>';
|
||||
$this->assertTrue(strstr($json[$ids[0]], $expected), 'The "Edit" contextual link is shown for the default revision.');
|
||||
$expected = '<li class="entitynodedelete-form"><a href="' . base_path() . 'node/' . $node->id() . '/delete">Delete</a></li>';
|
||||
$this->assertTrue(strstr($json[$ids[0]], $expected), 'The "Delete" contextual link is shown for the default revision.');
|
||||
|
||||
// Confirm that revisions revert properly.
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionid() . "/revert", [], t('Revert'));
|
||||
$this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.', [
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[1]->label(),
|
||||
'%revision-date' => format_date($nodes[1]->getRevisionCreationTime()),
|
||||
]), 'Revision reverted.');
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$reverted_node = $node_storage->load($node->id());
|
||||
$this->assertTrue(($nodes[1]->body->value == $reverted_node->body->value), 'Node reverted correctly.');
|
||||
// Confirm the revision author is the user performing the revert.
|
||||
$this->assertTrue($reverted_node->getRevisionUserId() == $this->loggedInUser->id(), 'Node revision author is user performing revert.');
|
||||
// And that its not the revision author.
|
||||
$this->assertTrue($reverted_node->getRevisionUserId() != $nodes[1]->getRevisionUserId(), 'Node revision author is not original revision author.');
|
||||
|
||||
// Confirm that this is not the default version.
|
||||
$node = node_revision_load($node->getRevisionId());
|
||||
$this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the default one.');
|
||||
|
||||
// Confirm that "Edit" and "Delete" contextual links don't appear for
|
||||
// non-default revision.
|
||||
$ids = ['node_revision::node=' . $node->id() . '&node_revision=' . $node->getRevisionId() . ':'];
|
||||
$json = $this->renderContextualLinks($ids, 'node/' . $node->id() . '/revisions/' . $node->getRevisionId() . '/view');
|
||||
$this->verbose($json[$ids[0]]);
|
||||
|
||||
$this->assertFalse(strstr($json[$ids[0]], '<li class="entitynodeedit-form">'), 'The "Edit" contextual link is not shown for a non-default revision.');
|
||||
$this->assertFalse(strstr($json[$ids[0]], '<li class="entitynodedelete-form">'), 'The "Delete" contextual link is not shown for a non-default revision.');
|
||||
|
||||
// Confirm revisions delete properly.
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionId() . "/delete", [], t('Delete'));
|
||||
$this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.', [
|
||||
'%revision-date' => format_date($nodes[1]->getRevisionCreationTime()),
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[1]->label(),
|
||||
]), 'Revision deleted.');
|
||||
$this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid and vid = :vid', [':nid' => $node->id(), ':vid' => $nodes[1]->getRevisionId()])->fetchField() == 0, 'Revision not found.');
|
||||
$this->assertTrue(db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid and vid = :vid', [':nid' => $node->id(), ':vid' => $nodes[1]->getRevisionId()])->fetchField() == 0, 'Field revision not found.');
|
||||
|
||||
// Set the revision timestamp to an older date to make sure that the
|
||||
// confirmation message correctly displays the stored revision date.
|
||||
$old_revision_date = REQUEST_TIME - 86400;
|
||||
db_update('node_revision')
|
||||
->condition('vid', $nodes[2]->getRevisionId())
|
||||
->fields([
|
||||
'revision_timestamp' => $old_revision_date,
|
||||
])
|
||||
->execute();
|
||||
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[2]->getRevisionId() . "/revert", [], t('Revert'));
|
||||
$this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.', [
|
||||
'@type' => 'Basic page',
|
||||
'%title' => $nodes[2]->label(),
|
||||
'%revision-date' => format_date($old_revision_date),
|
||||
]));
|
||||
|
||||
// Make a new revision and set it to not be default.
|
||||
// This will create a new revision that is not "front facing".
|
||||
$new_node_revision = clone $node;
|
||||
$new_body = $this->randomMachineName();
|
||||
$new_node_revision->body->value = $new_body;
|
||||
// Save this as a non-default revision.
|
||||
$new_node_revision->setNewRevision();
|
||||
$new_node_revision->isDefaultRevision = FALSE;
|
||||
$new_node_revision->save();
|
||||
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertNoText($new_body, 'Revision body text is not present on default version of node.');
|
||||
|
||||
// Verify that the new body text is present on the revision.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions/" . $new_node_revision->getRevisionId() . "/view");
|
||||
$this->assertText($new_body, 'Revision body text is present when loading specific revision.');
|
||||
|
||||
// Verify that the non-default revision vid is greater than the default
|
||||
// revision vid.
|
||||
$default_revision = db_select('node', 'n')
|
||||
->fields('n', ['vid'])
|
||||
->condition('nid', $node->id())
|
||||
->execute()
|
||||
->fetchCol();
|
||||
$default_revision_vid = $default_revision[0];
|
||||
$this->assertTrue($new_node_revision->getRevisionId() > $default_revision_vid, 'Revision vid is greater than default revision vid.');
|
||||
|
||||
// Create an 'EN' node with a revision log message.
|
||||
$node = $this->drupalCreateNode();
|
||||
$node->title = 'Node title in EN';
|
||||
$node->revision_log = 'Simple revision message (EN)';
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Create a new revision and new log message.
|
||||
$node = Node::load($node->id());
|
||||
$node->body->value = 'New text (EN)';
|
||||
$node->revision_log = 'New revision message (EN)';
|
||||
$node->setNewRevision();
|
||||
$node->save();
|
||||
|
||||
// Check both revisions are shown on the node revisions overview page.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertText('Simple revision message (EN)');
|
||||
$this->assertText('New revision message (EN)');
|
||||
|
||||
// Create an 'EN' node with a revision log message.
|
||||
$node = $this->drupalCreateNode();
|
||||
$node->langcode = 'en';
|
||||
$node->title = 'Node title in EN';
|
||||
$node->revision_log = 'Simple revision message (EN)';
|
||||
$node->save();
|
||||
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertResponse(403);
|
||||
|
||||
// Add a translation in 'DE' and create a new revision and new log message.
|
||||
$translation = $node->addTranslation('de');
|
||||
$translation->title->value = 'Node title in DE';
|
||||
$translation->body->value = 'New text (DE)';
|
||||
$translation->revision_log = 'New revision message (DE)';
|
||||
$translation->setNewRevision();
|
||||
$translation->save();
|
||||
|
||||
// View the revision UI in 'IT', only the original node revision is shown.
|
||||
$this->drupalGet("it/node/" . $node->id() . "/revisions");
|
||||
$this->assertText('Simple revision message (EN)');
|
||||
$this->assertNoText('New revision message (DE)');
|
||||
|
||||
// View the revision UI in 'DE', only the translated node revision is shown.
|
||||
$this->drupalGet("de/node/" . $node->id() . "/revisions");
|
||||
$this->assertNoText('Simple revision message (EN)');
|
||||
$this->assertText('New revision message (DE)');
|
||||
|
||||
// View the revision UI in 'EN', only the original node revision is shown.
|
||||
$this->drupalGet("node/" . $node->id() . "/revisions");
|
||||
$this->assertText('Simple revision message (EN)');
|
||||
$this->assertNoText('New revision message (DE)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that revisions are correctly saved without log messages.
|
||||
*/
|
||||
public function testNodeRevisionWithoutLogMessage() {
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
// Create a node with an initial log message.
|
||||
$revision_log = $this->randomMachineName(10);
|
||||
$node = $this->drupalCreateNode(['revision_log' => $revision_log]);
|
||||
|
||||
// Save over the same revision and explicitly provide an empty log message
|
||||
// (for example, to mimic the case of a node form submitted with no text in
|
||||
// the "log message" field), and check that the original log message is
|
||||
// preserved.
|
||||
$new_title = $this->randomMachineName(10) . 'testNodeRevisionWithoutLogMessage1';
|
||||
|
||||
$node = clone $node;
|
||||
$node->title = $new_title;
|
||||
$node->revision_log = '';
|
||||
$node->setNewRevision(FALSE);
|
||||
|
||||
$node->save();
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($new_title, 'New node title appears on the page.');
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$node_revision = $node_storage->load($node->id());
|
||||
$this->assertEqual($node_revision->revision_log->value, $revision_log, 'After an existing node revision is re-saved without a log message, the original log message is preserved.');
|
||||
|
||||
// Create another node with an initial revision log message.
|
||||
$node = $this->drupalCreateNode(['revision_log' => $revision_log]);
|
||||
|
||||
// Save a new node revision without providing a log message, and check that
|
||||
// this revision has an empty log message.
|
||||
$new_title = $this->randomMachineName(10) . 'testNodeRevisionWithoutLogMessage2';
|
||||
|
||||
$node = clone $node;
|
||||
$node->title = $new_title;
|
||||
$node->setNewRevision();
|
||||
$node->revision_log = NULL;
|
||||
|
||||
$node->save();
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($new_title, 'New node title appears on the page.');
|
||||
$node_storage->resetCache([$node->id()]);
|
||||
$node_revision = $node_storage->load($node->id());
|
||||
$this->assertTrue(empty($node_revision->revision_log->value), 'After a new node revision is saved with an empty log message, the log message for the node is empty.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets server-rendered contextual links for the given contextual links IDs.
|
||||
*
|
||||
* @param string[] $ids
|
||||
* An array of contextual link IDs.
|
||||
* @param string $current_path
|
||||
* The Drupal path for the page for which the contextual links are rendered.
|
||||
*
|
||||
* @return string
|
||||
* The decoded JSON response body.
|
||||
*/
|
||||
protected function renderContextualLinks(array $ids, $current_path) {
|
||||
$post = [];
|
||||
for ($i = 0; $i < count($ids); $i++) {
|
||||
$post['ids[' . $i . ']'] = $ids[$i];
|
||||
}
|
||||
$response = $this->drupalPost('contextual/render', 'application/json', $post, ['query' => ['destination' => $current_path]]);
|
||||
|
||||
return Json::decode($response);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the revision translations are correctly reverted.
|
||||
*/
|
||||
public function testRevisionTranslationRevert() {
|
||||
// Create a node and a few revisions.
|
||||
$node = $this->drupalCreateNode(['langcode' => 'en']);
|
||||
|
||||
$initial_revision_id = $node->getRevisionId();
|
||||
$initial_title = $node->label();
|
||||
$this->createRevisions($node, 2);
|
||||
|
||||
// Translate the node and create a few translation revisions.
|
||||
$translation = $node->addTranslation('it');
|
||||
$this->createRevisions($translation, 3);
|
||||
$revert_id = $node->getRevisionId();
|
||||
$translated_title = $translation->label();
|
||||
$untranslatable_string = $node->untranslatable_string_field->value;
|
||||
|
||||
// Create a new revision for the default translation in-between a series of
|
||||
// translation revisions.
|
||||
$this->createRevisions($node, 1);
|
||||
$default_translation_title = $node->label();
|
||||
|
||||
// And create a few more translation revisions.
|
||||
$this->createRevisions($translation, 2);
|
||||
$translation_revision_id = $translation->getRevisionId();
|
||||
|
||||
// Now revert the a translation revision preceding the last default
|
||||
// translation revision, and check that the desired value was reverted but
|
||||
// the default translation value was preserved.
|
||||
$revert_translation_url = Url::fromRoute('node.revision_revert_translation_confirm', [
|
||||
'node' => $node->id(),
|
||||
'node_revision' => $revert_id,
|
||||
'langcode' => 'it',
|
||||
]);
|
||||
$this->drupalPostForm($revert_translation_url, [], t('Revert'));
|
||||
/** @var \Drupal\node\NodeStorage $node_storage */
|
||||
$node_storage = $this->container->get('entity.manager')->getStorage('node');
|
||||
$node_storage->resetCache();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($node->id());
|
||||
$this->assertTrue($node->getRevisionId() > $translation_revision_id);
|
||||
$this->assertEqual($node->label(), $default_translation_title);
|
||||
$this->assertEqual($node->getTranslation('it')->label(), $translated_title);
|
||||
$this->assertNotEqual($node->untranslatable_string_field->value, $untranslatable_string);
|
||||
|
||||
$latest_revision_id = $translation->getRevisionId();
|
||||
|
||||
// Now revert the a translation revision preceding the last default
|
||||
// translation revision again, and check that the desired value was reverted
|
||||
// but the default translation value was preserved. But in addition the
|
||||
// untranslated field will be reverted as well.
|
||||
$this->drupalPostForm($revert_translation_url, ['revert_untranslated_fields' => TRUE], t('Revert'));
|
||||
$node_storage->resetCache();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($node->id());
|
||||
$this->assertTrue($node->getRevisionId() > $latest_revision_id);
|
||||
$this->assertEqual($node->label(), $default_translation_title);
|
||||
$this->assertEqual($node->getTranslation('it')->label(), $translated_title);
|
||||
$this->assertEqual($node->untranslatable_string_field->value, $untranslatable_string);
|
||||
|
||||
$latest_revision_id = $translation->getRevisionId();
|
||||
|
||||
// Now revert the entity revision to the initial one where the translation
|
||||
// didn't exist.
|
||||
$revert_url = Url::fromRoute('node.revision_revert_confirm', [
|
||||
'node' => $node->id(),
|
||||
'node_revision' => $initial_revision_id,
|
||||
]);
|
||||
$this->drupalPostForm($revert_url, [], t('Revert'));
|
||||
$node_storage->resetCache();
|
||||
/** @var \Drupal\node\NodeInterface $node */
|
||||
$node = $node_storage->load($node->id());
|
||||
$this->assertTrue($node->getRevisionId() > $latest_revision_id);
|
||||
$this->assertEqual($node->label(), $initial_title);
|
||||
$this->assertFalse($node->hasTranslation('it'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a series of revisions for the specified node.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The node object.
|
||||
* @param $count
|
||||
* The number of revisions to be created.
|
||||
*/
|
||||
protected function createRevisions(NodeInterface $node, $count) {
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$node->title = $this->randomString();
|
||||
$node->untranslatable_string_field->value = $this->randomString();
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\node\Tests;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\system\Tests\Menu\AssertBreadcrumbTrait;
|
||||
|
||||
/**
|
||||
* Ensures that node type functions work correctly.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeTypeTest extends NodeTestBase {
|
||||
|
||||
use AssertBreadcrumbTrait;
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['field_ui', 'block'];
|
||||
|
||||
/**
|
||||
* Ensures that node type functions (node_type_get_*) work correctly.
|
||||
*
|
||||
* Load available node types and validate the returned data.
|
||||
*/
|
||||
public function testNodeTypeGetFunctions() {
|
||||
$node_types = NodeType::loadMultiple();
|
||||
$node_names = node_type_get_names();
|
||||
|
||||
$this->assertTrue(isset($node_types['article']), 'Node type article is available.');
|
||||
$this->assertTrue(isset($node_types['page']), 'Node type basic page is available.');
|
||||
|
||||
$this->assertEqual($node_types['article']->label(), $node_names['article'], 'Correct node type base has been returned.');
|
||||
|
||||
$article = NodeType::load('article');
|
||||
$this->assertEqual($node_types['article'], $article, 'Correct node type has been returned.');
|
||||
$this->assertEqual($node_types['article']->label(), $article->label(), 'Correct node type name has been returned.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests creating a content type programmatically and via a form.
|
||||
*/
|
||||
public function testNodeTypeCreation() {
|
||||
// Create a content type programmatically.
|
||||
$type = $this->drupalCreateContentType();
|
||||
|
||||
$type_exists = (bool) NodeType::load($type->id());
|
||||
$this->assertTrue($type_exists, 'The new content type has been created in the database.');
|
||||
|
||||
// Log in a test user.
|
||||
$web_user = $this->drupalCreateUser(['create ' . $type->label() . ' content']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->drupalGet('node/add/' . $type->id());
|
||||
$this->assertResponse(200, 'The new content type can be accessed at node/add.');
|
||||
|
||||
// Create a content type via the user interface.
|
||||
$web_user = $this->drupalCreateUser(['bypass node access', 'administer content types']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertCacheTag('config:node_type_list');
|
||||
$this->assertCacheContext('user.permissions');
|
||||
$elements = $this->cssSelect('dl.node-type-list dt');
|
||||
$this->assertEqual(3, count($elements));
|
||||
|
||||
$edit = [
|
||||
'name' => 'foo',
|
||||
'title_label' => 'title for foo',
|
||||
'type' => 'foo',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/add', $edit, t('Save and manage fields'));
|
||||
$type_exists = (bool) NodeType::load('foo');
|
||||
$this->assertTrue($type_exists, 'The new content type has been created in the database.');
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$elements = $this->cssSelect('dl.node-type-list dt');
|
||||
$this->assertEqual(4, count($elements));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests editing a node type using the UI.
|
||||
*/
|
||||
public function testNodeTypeEditing() {
|
||||
$this->drupalPlaceBlock('system_breadcrumb_block');
|
||||
$web_user = $this->drupalCreateUser(['bypass node access', 'administer content types', 'administer node fields']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
$field = FieldConfig::loadByName('node', 'page', 'body');
|
||||
$this->assertEqual($field->getLabel(), 'Body', 'Body field was found.');
|
||||
|
||||
// Verify that title and body fields are displayed.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertRaw('Title', 'Title field was found.');
|
||||
$this->assertRaw('Body', 'Body field was found.');
|
||||
|
||||
// Rename the title field.
|
||||
$edit = [
|
||||
'title_label' => 'Foo',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertRaw('Foo', 'New title label was displayed.');
|
||||
$this->assertNoRaw('Title', 'Old title label was not displayed.');
|
||||
|
||||
// Change the name and the description.
|
||||
$edit = [
|
||||
'name' => 'Bar',
|
||||
'description' => 'Lorem ipsum.',
|
||||
];
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
|
||||
|
||||
$this->drupalGet('node/add');
|
||||
$this->assertRaw('Bar', 'New name was displayed.');
|
||||
$this->assertRaw('Lorem ipsum', 'New description was displayed.');
|
||||
$this->clickLink('Bar');
|
||||
$this->assertRaw('Foo', 'Title field was found.');
|
||||
$this->assertRaw('Body', 'Body field was found.');
|
||||
|
||||
// Change the name through the API
|
||||
/** @var \Drupal\node\NodeTypeInterface $node_type */
|
||||
$node_type = NodeType::load('page');
|
||||
$node_type->set('name', 'NewBar');
|
||||
$node_type->save();
|
||||
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info */
|
||||
$bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
$node_bundles = $bundle_info->getBundleInfo('node');
|
||||
$this->assertEqual($node_bundles['page']['label'], 'NewBar', 'Node type bundle cache is updated');
|
||||
|
||||
// Remove the body field.
|
||||
$this->drupalPostForm('admin/structure/types/manage/page/fields/node.page.body/delete', [], t('Delete'));
|
||||
// Resave the settings for this type.
|
||||
$this->drupalPostForm('admin/structure/types/manage/page', [], t('Save content type'));
|
||||
$front_page_path = Url::fromRoute('<front>')->toString();
|
||||
$this->assertBreadcrumb('admin/structure/types/manage/page/fields', [
|
||||
$front_page_path => 'Home',
|
||||
'admin/structure/types' => 'Content types',
|
||||
'admin/structure/types/manage/page' => 'NewBar',
|
||||
]);
|
||||
// Check that the body field doesn't exist.
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertNoRaw('Body', 'Body field was not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests deleting a content type that still has content.
|
||||
*/
|
||||
public function testNodeTypeDeletion() {
|
||||
$this->drupalPlaceBlock('page_title_block');
|
||||
// Create a content type programmatically.
|
||||
$type = $this->drupalCreateContentType();
|
||||
|
||||
// Log in a test user.
|
||||
$web_user = $this->drupalCreateUser([
|
||||
'bypass node access',
|
||||
'administer content types',
|
||||
]);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Add a new node of this type.
|
||||
$node = $this->drupalCreateNode(['type' => $type->id()]);
|
||||
// Attempt to delete the content type, which should not be allowed.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type->label() . '/delete');
|
||||
$this->assertRaw(
|
||||
t('%type is used by 1 piece of content on your site. You can not remove this content type until you have removed all of the %type content.', ['%type' => $type->label()]),
|
||||
'The content type will not be deleted until all nodes of that type are removed.'
|
||||
);
|
||||
$this->assertNoText(t('This action cannot be undone.'), 'The node type deletion confirmation form is not available.');
|
||||
|
||||
// Delete the node.
|
||||
$node->delete();
|
||||
// Attempt to delete the content type, which should now be allowed.
|
||||
$this->drupalGet('admin/structure/types/manage/' . $type->label() . '/delete');
|
||||
$this->assertRaw(
|
||||
t('Are you sure you want to delete the content type %type?', ['%type' => $type->label()]),
|
||||
'The content type is available for deletion.'
|
||||
);
|
||||
$this->assertText(t('This action cannot be undone.'), 'The node type deletion confirmation form is available.');
|
||||
|
||||
// Test that a locked node type could not be deleted.
|
||||
$this->container->get('module_installer')->install(['node_test_config']);
|
||||
// Lock the default node type.
|
||||
$locked = \Drupal::state()->get('node.type.locked');
|
||||
$locked['default'] = 'default';
|
||||
\Drupal::state()->set('node.type.locked', $locked);
|
||||
// Call to flush all caches after installing the forum module in the same
|
||||
// way installing a module through the UI does.
|
||||
$this->resetAll();
|
||||
$this->drupalGet('admin/structure/types/manage/default');
|
||||
$this->assertNoLink(t('Delete'));
|
||||
$this->drupalGet('admin/structure/types/manage/default/delete');
|
||||
$this->assertResponse(403);
|
||||
$this->container->get('module_installer')->uninstall(['node_test_config']);
|
||||
$this->container = \Drupal::getContainer();
|
||||
unset($locked['default']);
|
||||
\Drupal::state()->set('node.type.locked', $locked);
|
||||
$this->drupalGet('admin/structure/types/manage/default');
|
||||
$this->clickLink(t('Delete'));
|
||||
$this->assertResponse(200);
|
||||
$this->drupalPostForm(NULL, [], t('Delete'));
|
||||
$this->assertFalse((bool) NodeType::load('default'), 'Node type with machine default deleted.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests Field UI integration for content types.
|
||||
*/
|
||||
public function testNodeTypeFieldUiPermissions() {
|
||||
// Create an admin user who can only manage node fields.
|
||||
$admin_user_1 = $this->drupalCreateUser(['administer content types', 'administer node fields']);
|
||||
$this->drupalLogin($admin_user_1);
|
||||
|
||||
// Test that the user only sees the actions available to him.
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertLinkByHref('admin/structure/types/manage/article/fields');
|
||||
$this->assertNoLinkByHref('admin/structure/types/manage/article/display');
|
||||
|
||||
// Create another admin user who can manage node fields display.
|
||||
$admin_user_2 = $this->drupalCreateUser(['administer content types', 'administer node display']);
|
||||
$this->drupalLogin($admin_user_2);
|
||||
|
||||
// Test that the user only sees the actions available to him.
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertNoLinkByHref('admin/structure/types/manage/article/fields');
|
||||
$this->assertLinkByHref('admin/structure/types/manage/article/display');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for when there are no content types defined.
|
||||
*/
|
||||
public function testNodeTypeNoContentType() {
|
||||
/** @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info */
|
||||
$bundle_info = \Drupal::service('entity_type.bundle.info');
|
||||
$this->assertEqual(2, count($bundle_info->getBundleInfo('node')), 'The bundle information service has 2 bundles for the Node entity type.');
|
||||
$web_user = $this->drupalCreateUser(['administer content types']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Delete 'article' bundle.
|
||||
$this->drupalPostForm('admin/structure/types/manage/article/delete', [], t('Delete'));
|
||||
// Delete 'page' bundle.
|
||||
$this->drupalPostForm('admin/structure/types/manage/page/delete', [], t('Delete'));
|
||||
|
||||
// Navigate to content type administration screen
|
||||
$this->drupalGet('admin/structure/types');
|
||||
$this->assertRaw(t('No content types available. <a href=":link">Add content type</a>.', [
|
||||
':link' => Url::fromRoute('node.type_add')->toString(),
|
||||
]), 'Empty text when there are no content types in the system is correct.');
|
||||
|
||||
$bundle_info->clearCachedBundles();
|
||||
$this->assertEqual(0, count($bundle_info->getBundleInfo('node')), 'The bundle information service has 0 bundles for the Node entity type.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\node\Tests;
|
||||
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\node\Entity\NodeType;
|
||||
use Drupal\taxonomy\Entity\Term;
|
||||
use Drupal\taxonomy\Entity\Vocabulary;
|
||||
|
||||
/**
|
||||
* Tests the node entity preview functionality.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class PagePreviewTest extends NodeTestBase {
|
||||
|
||||
use EntityReferenceTestTrait;
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Enable the comment, node and taxonomy modules to test on the preview.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['node', 'taxonomy', 'comment', 'image', 'file', 'text', 'node_test', 'menu_ui'];
|
||||
|
||||
/**
|
||||
* The name of the created field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fieldName;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->addDefaultCommentField('node', 'page');
|
||||
|
||||
$web_user = $this->drupalCreateUser(['edit own page content', 'create page content', 'administer menu']);
|
||||
$this->drupalLogin($web_user);
|
||||
|
||||
// Add a vocabulary so we can test different view modes.
|
||||
$vocabulary = Vocabulary::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => $this->randomMachineName(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
'help' => '',
|
||||
]);
|
||||
$vocabulary->save();
|
||||
|
||||
$this->vocabulary = $vocabulary;
|
||||
|
||||
// Add a term to the vocabulary.
|
||||
$term = Term::create([
|
||||
'name' => $this->randomMachineName(),
|
||||
'description' => $this->randomMachineName(),
|
||||
'vid' => $this->vocabulary->id(),
|
||||
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
|
||||
]);
|
||||
$term->save();
|
||||
|
||||
$this->term = $term;
|
||||
|
||||
// Create an image field.
|
||||
FieldStorageConfig::create([
|
||||
'field_name' => 'field_image',
|
||||
'entity_type' => 'node',
|
||||
'type' => 'image',
|
||||
'settings' => [],
|
||||
'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
|
||||
])->save();
|
||||
|
||||
$field_config = FieldConfig::create([
|
||||
'field_name' => 'field_image',
|
||||
'label' => 'Images',
|
||||
'entity_type' => 'node',
|
||||
'bundle' => 'page',
|
||||
'required' => FALSE,
|
||||
'settings' => [],
|
||||
]);
|
||||
$field_config->save();
|
||||
|
||||
// Create a field.
|
||||
$this->fieldName = mb_strtolower($this->randomMachineName());
|
||||
$handler_settings = [
|
||||
'target_bundles' => [
|
||||
$this->vocabulary->id() => $this->vocabulary->id(),
|
||||
],
|
||||
'auto_create' => TRUE,
|
||||
];
|
||||
$this->createEntityReferenceField('node', 'page', $this->fieldName, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'entity_reference_autocomplete_tags',
|
||||
])
|
||||
->save();
|
||||
|
||||
// Show on default display and teaser.
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'entity_reference_label',
|
||||
])
|
||||
->save();
|
||||
entity_get_display('node', 'page', 'teaser')
|
||||
->setComponent($this->fieldName, [
|
||||
'type' => 'entity_reference_label',
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent('field_image', [
|
||||
'type' => 'image_image',
|
||||
'settings' => [],
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent('field_image')
|
||||
->save();
|
||||
|
||||
// Create a multi-value text field.
|
||||
$field_storage = FieldStorageConfig::create([
|
||||
'field_name' => 'field_test_multi',
|
||||
'entity_type' => 'node',
|
||||
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
|
||||
'type' => 'text',
|
||||
'settings' => [
|
||||
'max_length' => 50,
|
||||
],
|
||||
]);
|
||||
$field_storage->save();
|
||||
FieldConfig::create([
|
||||
'field_storage' => $field_storage,
|
||||
'bundle' => 'page',
|
||||
])->save();
|
||||
|
||||
entity_get_form_display('node', 'page', 'default')
|
||||
->setComponent('field_test_multi', [
|
||||
'type' => 'text_textfield',
|
||||
])
|
||||
->save();
|
||||
|
||||
entity_get_display('node', 'page', 'default')
|
||||
->setComponent('field_test_multi', [
|
||||
'type' => 'string',
|
||||
])
|
||||
->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview functionality.
|
||||
*/
|
||||
public function testPagePreview() {
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
$term_key = $this->fieldName . '[target_id]';
|
||||
|
||||
// Fill in node creation form and preview node.
|
||||
$edit = [];
|
||||
$edit[$title_key] = '<em>' . $this->randomMachineName(8) . '</em>';
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$edit[$term_key] = $this->term->getName();
|
||||
|
||||
// Upload an image.
|
||||
$test_image = current($this->drupalGetTestFiles('image', 39325));
|
||||
$edit['files[field_image_0][]'] = \Drupal::service('file_system')->realpath($test_image->uri);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Upload'));
|
||||
|
||||
// Add an alt tag and preview the node.
|
||||
$this->drupalPostForm(NULL, ['field_image[0][alt]' => 'Picture of llamas'], t('Preview'));
|
||||
|
||||
// Check that the preview is displaying the title, body and term.
|
||||
$this->assertTitle(t('@title | Drupal', ['@title' => $edit[$title_key]]), 'Basic page title is preview.');
|
||||
$this->assertEscaped($edit[$title_key], 'Title displayed and escaped.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
$this->assertLink(t('Back to content editing'));
|
||||
|
||||
// Check that we see the class of the node type on the body element.
|
||||
$body_class_element = $this->xpath("//body[contains(@class, 'page-node-type-page')]");
|
||||
$this->assertTrue(!empty($body_class_element), 'Node type body class found.');
|
||||
|
||||
// Get the UUID.
|
||||
$url = parse_url($this->getUrl());
|
||||
$paths = explode('/', $url['path']);
|
||||
$view_mode = array_pop($paths);
|
||||
$uuid = array_pop($paths);
|
||||
|
||||
// Switch view mode. We'll remove the body from the teaser view mode.
|
||||
entity_get_display('node', 'page', 'teaser')
|
||||
->removeComponent('body')
|
||||
->save();
|
||||
|
||||
$view_mode_edit = ['view_mode' => 'teaser'];
|
||||
$this->drupalPostForm('node/preview/' . $uuid . '/full', $view_mode_edit, t('Switch'));
|
||||
$this->assertRaw('view-mode-teaser', 'View mode teaser class found.');
|
||||
$this->assertNoText($edit[$body_key], 'Body not displayed.');
|
||||
|
||||
// Check that the title, body and term fields are displayed with the
|
||||
// values after going back to the content edit page.
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
|
||||
$this->assertFieldByName('field_image[0][alt]', 'Picture of llamas');
|
||||
$this->drupalPostAjaxForm(NULL, [], ['field_test_multi_add_more' => t('Add another item')], NULL, [], [], 'node-page-form');
|
||||
$this->assertFieldByName('field_test_multi[0][value]');
|
||||
$this->assertFieldByName('field_test_multi[1][value]');
|
||||
|
||||
// Return to page preview to check everything is as expected.
|
||||
$this->drupalPostForm(NULL, [], t('Preview'));
|
||||
$this->assertTitle(t('@title | Drupal', ['@title' => $edit[$title_key]]), 'Basic page title is preview.');
|
||||
$this->assertEscaped($edit[$title_key], 'Title displayed and escaped.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
$this->assertLink(t('Back to content editing'));
|
||||
|
||||
// Assert the content is kept when reloading the page.
|
||||
$this->drupalGet('node/add/page', ['query' => ['uuid' => $uuid]]);
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
|
||||
|
||||
// Save the node - this is a new POST, so we need to upload the image.
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Upload'));
|
||||
$this->drupalPostForm(NULL, ['field_image[0][alt]' => 'Picture of llamas'], t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
|
||||
// Check the term was displayed on the saved node.
|
||||
$this->drupalGet('node/' . $node->id());
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
|
||||
// Check the term appears again on the edit form.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key] . ' (' . $this->term->id() . ')', 'Term field displayed.');
|
||||
|
||||
// Check with two new terms on the edit form, additionally to the existing
|
||||
// one.
|
||||
$edit = [];
|
||||
$newterm1 = $this->randomMachineName(8);
|
||||
$newterm2 = $this->randomMachineName(8);
|
||||
$edit[$term_key] = $this->term->getName() . ', ' . $newterm1 . ', ' . $newterm2;
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertRaw('>' . $newterm1 . '<', 'First new term displayed.');
|
||||
$this->assertRaw('>' . $newterm2 . '<', 'Second new term displayed.');
|
||||
// The first term should be displayed as link, the others not.
|
||||
$this->assertLink($this->term->getName());
|
||||
$this->assertNoLink($newterm1);
|
||||
$this->assertNoLink($newterm2);
|
||||
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
|
||||
|
||||
// Check with one more new term, keeping old terms, removing the existing
|
||||
// one.
|
||||
$edit = [];
|
||||
$newterm3 = $this->randomMachineName(8);
|
||||
$edit[$term_key] = $newterm1 . ', ' . $newterm3 . ', ' . $newterm2;
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertRaw('>' . $newterm1 . '<', 'First existing term displayed.');
|
||||
$this->assertRaw('>' . $newterm2 . '<', 'Second existing term displayed.');
|
||||
$this->assertRaw('>' . $newterm3 . '<', 'Third new term displayed.');
|
||||
$this->assertNoText($this->term->getName());
|
||||
$this->assertLink($newterm1);
|
||||
$this->assertLink($newterm2);
|
||||
$this->assertNoLink($newterm3);
|
||||
|
||||
// Check that editing an existing node after it has been previewed and not
|
||||
// saved doesn't remember the previous changes.
|
||||
$edit = [
|
||||
$title_key => $this->randomMachineName(8),
|
||||
];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertText($edit[$title_key], 'New title displayed.');
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName($title_key, $edit[$title_key], 'New title value displayed.');
|
||||
// Navigate away from the node without saving.
|
||||
$this->drupalGet('<front>');
|
||||
// Go back to the edit form, the title should have its initial value.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName($title_key, $node->label(), 'Correct title value displayed.');
|
||||
|
||||
// Check with required preview.
|
||||
$node_type = NodeType::load('page');
|
||||
$node_type->setPreviewMode(DRUPAL_REQUIRED);
|
||||
$node_type->save();
|
||||
$this->drupalGet('node/add/page');
|
||||
$this->assertNoRaw('edit-submit');
|
||||
$this->drupalPostForm('node/add/page', [$title_key => 'Preview'], t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertRaw('edit-submit');
|
||||
|
||||
// Check that destination is remembered when clicking on preview. When going
|
||||
// back to the edit form and clicking save, we should go back to the
|
||||
// original destination, if set.
|
||||
$destination = 'node';
|
||||
$this->drupalPostForm($node->toUrl('edit-form'), [], t('Preview'), ['query' => ['destination' => $destination]]);
|
||||
$parameters = ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'];
|
||||
$options = ['absolute' => TRUE, 'query' => ['destination' => $destination]];
|
||||
$this->assertUrl(Url::fromRoute('entity.node.preview', $parameters, $options));
|
||||
$this->drupalPostForm(NULL, ['view_mode' => 'teaser'], t('Switch'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertUrl($destination);
|
||||
|
||||
// Check that preview page works as expected without a destination set.
|
||||
$this->drupalPostForm($node->toUrl('edit-form'), [], t('Preview'));
|
||||
$parameters = ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'];
|
||||
$this->assertUrl(Url::fromRoute('entity.node.preview', $parameters, ['absolute' => TRUE]));
|
||||
$this->drupalPostForm(NULL, ['view_mode' => 'teaser'], t('Switch'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertUrl($node->toUrl());
|
||||
$this->assertResponse(200);
|
||||
|
||||
/** @var \Drupal\Core\File\FileSystemInterface $file_system */
|
||||
$file_system = \Drupal::service('file_system');
|
||||
// Assert multiple items can be added and are not lost when previewing.
|
||||
$test_image_1 = current($this->drupalGetTestFiles('image', 39325));
|
||||
$edit_image_1['files[field_image_0][]'] = $file_system->realpath($test_image_1->uri);
|
||||
$test_image_2 = current($this->drupalGetTestFiles('image', 39325));
|
||||
$edit_image_2['files[field_image_1][]'] = $file_system->realpath($test_image_2->uri);
|
||||
$edit['field_image[0][alt]'] = 'Alt 1';
|
||||
|
||||
$this->drupalPostForm('node/add/page', $edit_image_1, t('Upload'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName('files[field_image_1][]');
|
||||
$this->drupalPostForm(NULL, $edit_image_2, t('Upload'));
|
||||
$this->assertNoFieldByName('files[field_image_1][]');
|
||||
|
||||
$title = 'node_test_title';
|
||||
$example_text_1 = 'example_text_preview_1';
|
||||
$example_text_2 = 'example_text_preview_2';
|
||||
$example_text_3 = 'example_text_preview_3';
|
||||
$this->drupalGet('node/add/page');
|
||||
$edit = [
|
||||
'title[0][value]' => $title,
|
||||
'field_test_multi[0][value]' => $example_text_1,
|
||||
];
|
||||
$this->assertRaw('Storage is not set');
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertRaw('Storage is set');
|
||||
$this->assertFieldByName('field_test_multi[0][value]');
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertText('Basic page ' . $title . ' has been created.');
|
||||
$node = $this->drupalGetNodeByTitle($title);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->drupalPostAjaxForm(NULL, [], ['field_test_multi_add_more' => t('Add another item')]);
|
||||
$this->drupalPostAjaxForm(NULL, [], ['field_test_multi_add_more' => t('Add another item')]);
|
||||
$edit = [
|
||||
'field_test_multi[1][value]' => $example_text_2,
|
||||
'field_test_multi[2][value]' => $example_text_3,
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldByName('field_test_multi[0][value]', $example_text_1);
|
||||
$this->assertFieldByName('field_test_multi[1][value]', $example_text_2);
|
||||
$this->assertFieldByName('field_test_multi[2][value]', $example_text_3);
|
||||
|
||||
// Now save the node and make sure all values got saved.
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$this->assertText($example_text_1);
|
||||
$this->assertText($example_text_2);
|
||||
$this->assertText($example_text_3);
|
||||
|
||||
// Edit again, change the menu_ui settings and click on preview.
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$edit = [
|
||||
'menu[enabled]' => TRUE,
|
||||
'menu[title]' => 'Changed title',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Preview'));
|
||||
$this->clickLink(t('Back to content editing'));
|
||||
$this->assertFieldChecked('edit-menu-enabled', 'Menu option is still checked');
|
||||
$this->assertFieldByName('menu[title]', 'Changed title', 'Menu link title is correct after preview');
|
||||
|
||||
// Save, change the title while saving and make sure that it is correctly
|
||||
// saved.
|
||||
$edit = [
|
||||
'menu[enabled]' => TRUE,
|
||||
'menu[title]' => 'Second title change',
|
||||
];
|
||||
$this->drupalPostForm(NULL, $edit, t('Save'));
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName('menu[title]', 'Second title change', 'Menu link title is correct after saving');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview functionality, when using revisions.
|
||||
*/
|
||||
public function testPagePreviewWithRevisions() {
|
||||
$title_key = 'title[0][value]';
|
||||
$body_key = 'body[0][value]';
|
||||
$term_key = $this->fieldName . '[target_id]';
|
||||
// Force revision on "Basic page" content.
|
||||
$node_type = NodeType::load('page');
|
||||
$node_type->setNewRevision(TRUE);
|
||||
$node_type->save();
|
||||
|
||||
// Fill in node creation form and preview node.
|
||||
$edit = [];
|
||||
$edit[$title_key] = $this->randomMachineName(8);
|
||||
$edit[$body_key] = $this->randomMachineName(16);
|
||||
$edit[$term_key] = $this->term->id();
|
||||
$edit['revision_log[0][value]'] = $this->randomString(32);
|
||||
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
|
||||
|
||||
// Check that the preview is displaying the title, body and term.
|
||||
$this->assertTitle(t('@title | Drupal', ['@title' => $edit[$title_key]]), 'Basic page title is preview.');
|
||||
$this->assertText($edit[$title_key], 'Title displayed.');
|
||||
$this->assertText($edit[$body_key], 'Body displayed.');
|
||||
$this->assertText($edit[$term_key], 'Term displayed.');
|
||||
|
||||
// Check that the title and body fields are displayed with the correct
|
||||
// values after going back to the content edit page.
|
||||
$this->clickLink(t('Back to content editing')); $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
|
||||
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
|
||||
$this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
|
||||
|
||||
// Check that the revision log field has the correct value.
|
||||
$this->assertFieldByName('revision_log[0][value]', $edit['revision_log[0][value]'], 'Revision log field displayed.');
|
||||
|
||||
// Save the node after coming back from the preview page so we can create a
|
||||
// pending revision for it.
|
||||
$this->drupalPostForm(NULL, [], t('Save'));
|
||||
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
|
||||
|
||||
// Check that previewing a pending revision of a node works. This can not be
|
||||
// accomplished through the UI so we have to use API calls.
|
||||
// @todo Change this test to use the UI when we will be able to create
|
||||
// pending revisions in core.
|
||||
// @see https://www.drupal.org/node/2725533
|
||||
$node->setNewRevision(TRUE);
|
||||
$node->isDefaultRevision(FALSE);
|
||||
|
||||
/** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
|
||||
$controller_resolver = \Drupal::service('controller_resolver');
|
||||
$node_preview_controller = $controller_resolver->getControllerFromDefinition('\Drupal\node\Controller\NodePreviewController::view');
|
||||
$node_preview_controller($node, 'full');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the node preview accessible for simultaneous node editing.
|
||||
*/
|
||||
public function testSimultaneousPreview() {
|
||||
$title_key = 'title[0][value]';
|
||||
$node = $this->drupalCreateNode([]);
|
||||
|
||||
$edit = [$title_key => 'New page title'];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
|
||||
$this->assertText($edit[$title_key]);
|
||||
|
||||
$user2 = $this->drupalCreateUser(['edit any page content']);
|
||||
$this->drupalLogin($user2);
|
||||
$this->drupalGet('node/' . $node->id() . '/edit');
|
||||
$this->assertFieldByName($title_key, $node->label(), 'No title leaked from previous user.');
|
||||
|
||||
$edit2 = [$title_key => 'Another page title'];
|
||||
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit2, t('Preview'));
|
||||
$this->assertUrl(\Drupal::url('entity.node.preview', ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'], ['absolute' => TRUE]));
|
||||
$this->assertText($edit2[$title_key]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\node\Tests\Views;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\user\Entity\User;
|
||||
|
||||
/**
|
||||
* Tests views contextual links on nodes.
|
||||
*
|
||||
* @group node
|
||||
*/
|
||||
class NodeContextualLinksTest extends NodeTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['contextual'];
|
||||
|
||||
/**
|
||||
* Views used by this test.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = ['test_contextual_links'];
|
||||
|
||||
/**
|
||||
* Tests contextual links.
|
||||
*/
|
||||
public function testNodeContextualLinks() {
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$this->drupalCreateNode(['promote' => 1]);
|
||||
$this->drupalGet('node');
|
||||
|
||||
$user = $this->drupalCreateUser(['administer nodes', 'access contextual links']);
|
||||
$this->drupalLogin($user);
|
||||
|
||||
$response = $this->renderContextualLinks(['node:node=1:'], 'node');
|
||||
$this->assertResponse(200);
|
||||
$json = Json::decode($response);
|
||||
$this->setRawContent($json['node:node=1:']);
|
||||
|
||||
// @todo Add these back when the functionality for making Views displays
|
||||
// appear in contextual links is working again.
|
||||
// $this->assertLinkByHref('node/1/contextual-links', 0, 'The contextual link to the view was found.');
|
||||
// $this->assertLink('Test contextual link', 0, 'The contextual link to the view was found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server-rendered contextual links for the given contextual link ids.
|
||||
*
|
||||
* Copied from \Drupal\contextual\Tests\ContextualDynamicContextTest::renderContextualLinks().
|
||||
*
|
||||
* @param array $ids
|
||||
* An array of contextual link ids.
|
||||
* @param string $current_path
|
||||
* The Drupal path for the page for which the contextual links are rendered.
|
||||
*
|
||||
* @return string
|
||||
* The response body.
|
||||
*/
|
||||
protected function renderContextualLinks($ids, $current_path) {
|
||||
// Build POST values.
|
||||
$post = [];
|
||||
for ($i = 0; $i < count($ids); $i++) {
|
||||
$post['ids[' . $i . ']'] = $ids[$i];
|
||||
}
|
||||
|
||||
// Serialize POST values.
|
||||
foreach ($post as $key => $value) {
|
||||
// Encode according to application/x-www-form-urlencoded
|
||||
// Both names and values needs to be urlencoded, according to
|
||||
// http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
|
||||
$post[$key] = urlencode($key) . '=' . urlencode($value);
|
||||
}
|
||||
$post = implode('&', $post);
|
||||
|
||||
// Perform HTTP request.
|
||||
return $this->curlExec([
|
||||
CURLOPT_URL => \Drupal::url('contextual.render', [], ['absolute' => TRUE, 'query' => ['destination' => $current_path]]),
|
||||
CURLOPT_POST => TRUE,
|
||||
CURLOPT_POSTFIELDS => $post,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the node page works if Contextual Links is disabled.
|
||||
*
|
||||
* All views have Contextual links enabled by default, even with the
|
||||
* Contextual links module disabled. This tests if no calls are done to the
|
||||
* Contextual links module by views when it is disabled.
|
||||
*
|
||||
* @see https://www.drupal.org/node/2379811
|
||||
*/
|
||||
public function testPageWithDisabledContextualModule() {
|
||||
\Drupal::service('module_installer')->uninstall(['contextual']);
|
||||
\Drupal::service('module_installer')->install(['views_ui']);
|
||||
|
||||
// Ensure that contextual links don't get called for admin users.
|
||||
$admin_user = User::load(1);
|
||||
$admin_user->setPassword('new_password');
|
||||
$admin_user->pass_raw = 'new_password';
|
||||
$admin_user->save();
|
||||
|
||||
$this->drupalCreateContentType(['type' => 'page']);
|
||||
$this->drupalCreateNode(['promote' => 1]);
|
||||
|
||||
$this->drupalLogin($admin_user);
|
||||
$this->drupalGet('node');
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user