Mercurial > hg > isophonics-drupal-site
comparison core/modules/node/src/Tests/NodeRevisionsTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\node\Tests; | |
4 | |
5 use Drupal\Core\Url; | |
6 use Drupal\field\Entity\FieldConfig; | |
7 use Drupal\field\Entity\FieldStorageConfig; | |
8 use Drupal\language\Entity\ConfigurableLanguage; | |
9 use Drupal\node\Entity\Node; | |
10 use Drupal\node\NodeInterface; | |
11 use Drupal\Component\Serialization\Json; | |
12 | |
13 /** | |
14 * Create a node with revisions and test viewing, saving, reverting, and | |
15 * deleting revisions for users with access for this content type. | |
16 * | |
17 * @group node | |
18 */ | |
19 class NodeRevisionsTest extends NodeTestBase { | |
20 | |
21 /** | |
22 * An array of node revisions. | |
23 * | |
24 * @var \Drupal\node\NodeInterface[] | |
25 */ | |
26 protected $nodes; | |
27 | |
28 /** | |
29 * Revision log messages. | |
30 * | |
31 * @var array | |
32 */ | |
33 protected $revisionLogs; | |
34 | |
35 /** | |
36 * {@inheritdoc} | |
37 */ | |
38 public static $modules = ['node', 'contextual', 'datetime', 'language', 'content_translation']; | |
39 | |
40 /** | |
41 * {@inheritdoc} | |
42 */ | |
43 protected function setUp() { | |
44 parent::setUp(); | |
45 | |
46 // Enable additional languages. | |
47 ConfigurableLanguage::createFromLangcode('de')->save(); | |
48 ConfigurableLanguage::createFromLangcode('it')->save(); | |
49 | |
50 $field_storage_definition = [ | |
51 'field_name' => 'untranslatable_string_field', | |
52 'entity_type' => 'node', | |
53 'type' => 'string', | |
54 'cardinality' => 1, | |
55 'translatable' => FALSE, | |
56 ]; | |
57 $field_storage = FieldStorageConfig::create($field_storage_definition); | |
58 $field_storage->save(); | |
59 | |
60 $field_definition = [ | |
61 'field_storage' => $field_storage, | |
62 'bundle' => 'page', | |
63 ]; | |
64 $field = FieldConfig::create($field_definition); | |
65 $field->save(); | |
66 | |
67 // Create and log in user. | |
68 $web_user = $this->drupalCreateUser( | |
69 [ | |
70 'view page revisions', | |
71 'revert page revisions', | |
72 'delete page revisions', | |
73 'edit any page content', | |
74 'delete any page content', | |
75 'access contextual links', | |
76 'translate any entity', | |
77 'administer content types', | |
78 ] | |
79 ); | |
80 | |
81 $this->drupalLogin($web_user); | |
82 | |
83 // Create initial node. | |
84 $node = $this->drupalCreateNode(); | |
85 $settings = get_object_vars($node); | |
86 $settings['revision'] = 1; | |
87 $settings['isDefaultRevision'] = TRUE; | |
88 | |
89 $nodes = []; | |
90 $logs = []; | |
91 | |
92 // Get original node. | |
93 $nodes[] = clone $node; | |
94 | |
95 // Create three revisions. | |
96 $revision_count = 3; | |
97 for ($i = 0; $i < $revision_count; $i++) { | |
98 $logs[] = $node->revision_log = $this->randomMachineName(32); | |
99 | |
100 // Create revision with a random title and body and update variables. | |
101 $node->title = $this->randomMachineName(); | |
102 $node->body = [ | |
103 'value' => $this->randomMachineName(32), | |
104 'format' => filter_default_format(), | |
105 ]; | |
106 $node->untranslatable_string_field->value = $this->randomString(); | |
107 $node->setNewRevision(); | |
108 | |
109 // Edit the 2nd revision with a different user. | |
110 if ($i == 1) { | |
111 $editor = $this->drupalCreateUser(); | |
112 $node->setRevisionUserId($editor->id()); | |
113 } | |
114 else { | |
115 $node->setRevisionUserId($web_user->id()); | |
116 } | |
117 | |
118 $node->save(); | |
119 | |
120 // Make sure we get revision information. | |
121 $node = Node::load($node->id()); | |
122 $nodes[] = clone $node; | |
123 } | |
124 | |
125 $this->nodes = $nodes; | |
126 $this->revisionLogs = $logs; | |
127 } | |
128 | |
129 /** | |
130 * Checks node revision related operations. | |
131 */ | |
132 public function testRevisions() { | |
133 $node_storage = $this->container->get('entity.manager')->getStorage('node'); | |
134 $nodes = $this->nodes; | |
135 $logs = $this->revisionLogs; | |
136 | |
137 // Get last node for simple checks. | |
138 $node = $nodes[3]; | |
139 | |
140 // Confirm the correct revision text appears on "view revisions" page. | |
141 $this->drupalGet("node/" . $node->id() . "/revisions/" . $node->getRevisionId() . "/view"); | |
142 $this->assertText($node->body->value, 'Correct text displays for version.'); | |
143 | |
144 // Confirm the correct log message appears on "revisions overview" page. | |
145 $this->drupalGet("node/" . $node->id() . "/revisions"); | |
146 foreach ($logs as $revision_log) { | |
147 $this->assertText($revision_log, 'Revision log message found.'); | |
148 } | |
149 // Original author, and editor names should appear on revisions overview. | |
150 $web_user = $nodes[0]->revision_uid->entity; | |
151 $this->assertText(t('by @name', ['@name' => $web_user->getAccountName()])); | |
152 $editor = $nodes[2]->revision_uid->entity; | |
153 $this->assertText(t('by @name', ['@name' => $editor->getAccountName()])); | |
154 | |
155 // Confirm that this is the default revision. | |
156 $this->assertTrue($node->isDefaultRevision(), 'Third node revision is the default one.'); | |
157 | |
158 // Confirm that the "Edit" and "Delete" contextual links appear for the | |
159 // default revision. | |
160 $ids = ['node:node=' . $node->id() . ':changed=' . $node->getChangedTime()]; | |
161 $json = $this->renderContextualLinks($ids, 'node/' . $node->id()); | |
162 $this->verbose($json[$ids[0]]); | |
163 | |
164 $expected = '<li class="entitynodeedit-form"><a href="' . base_path() . 'node/' . $node->id() . '/edit">Edit</a></li>'; | |
165 $this->assertTrue(strstr($json[$ids[0]], $expected), 'The "Edit" contextual link is shown for the default revision.'); | |
166 $expected = '<li class="entitynodedelete-form"><a href="' . base_path() . 'node/' . $node->id() . '/delete">Delete</a></li>'; | |
167 $this->assertTrue(strstr($json[$ids[0]], $expected), 'The "Delete" contextual link is shown for the default revision.'); | |
168 | |
169 | |
170 // Confirm that revisions revert properly. | |
171 $this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionid() . "/revert", [], t('Revert')); | |
172 $this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.', [ | |
173 '@type' => 'Basic page', | |
174 '%title' => $nodes[1]->label(), | |
175 '%revision-date' => format_date($nodes[1]->getRevisionCreationTime()) | |
176 ]), 'Revision reverted.'); | |
177 $node_storage->resetCache([$node->id()]); | |
178 $reverted_node = $node_storage->load($node->id()); | |
179 $this->assertTrue(($nodes[1]->body->value == $reverted_node->body->value), 'Node reverted correctly.'); | |
180 | |
181 // Confirm that this is not the default version. | |
182 $node = node_revision_load($node->getRevisionId()); | |
183 $this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the default one.'); | |
184 | |
185 // Confirm that "Edit" and "Delete" contextual links don't appear for | |
186 // non-default revision. | |
187 $ids = ['node_revision::node=' . $node->id() . '&node_revision=' . $node->getRevisionId() . ':']; | |
188 $json = $this->renderContextualLinks($ids, 'node/' . $node->id() . '/revisions/' . $node->getRevisionId() . '/view'); | |
189 $this->verbose($json[$ids[0]]); | |
190 | |
191 $this->assertFalse(strstr($json[$ids[0]], '<li class="entitynodeedit-form">'), 'The "Edit" contextual link is not shown for a non-default revision.'); | |
192 $this->assertFalse(strstr($json[$ids[0]], '<li class="entitynodedelete-form">'), 'The "Delete" contextual link is not shown for a non-default revision.'); | |
193 | |
194 | |
195 // Confirm revisions delete properly. | |
196 $this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionId() . "/delete", [], t('Delete')); | |
197 $this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.', [ | |
198 '%revision-date' => format_date($nodes[1]->getRevisionCreationTime()), | |
199 '@type' => 'Basic page', | |
200 '%title' => $nodes[1]->label(), | |
201 ]), 'Revision deleted.'); | |
202 $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.'); | |
203 $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.'); | |
204 | |
205 // Set the revision timestamp to an older date to make sure that the | |
206 // confirmation message correctly displays the stored revision date. | |
207 $old_revision_date = REQUEST_TIME - 86400; | |
208 db_update('node_revision') | |
209 ->condition('vid', $nodes[2]->getRevisionId()) | |
210 ->fields([ | |
211 'revision_timestamp' => $old_revision_date, | |
212 ]) | |
213 ->execute(); | |
214 $this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[2]->getRevisionId() . "/revert", [], t('Revert')); | |
215 $this->assertRaw(t('@type %title has been reverted to the revision from %revision-date.', [ | |
216 '@type' => 'Basic page', | |
217 '%title' => $nodes[2]->label(), | |
218 '%revision-date' => format_date($old_revision_date), | |
219 ])); | |
220 | |
221 // Make a new revision and set it to not be default. | |
222 // This will create a new revision that is not "front facing". | |
223 $new_node_revision = clone $node; | |
224 $new_body = $this->randomMachineName(); | |
225 $new_node_revision->body->value = $new_body; | |
226 // Save this as a non-default revision. | |
227 $new_node_revision->setNewRevision(); | |
228 $new_node_revision->isDefaultRevision = FALSE; | |
229 $new_node_revision->save(); | |
230 | |
231 $this->drupalGet('node/' . $node->id()); | |
232 $this->assertNoText($new_body, 'Revision body text is not present on default version of node.'); | |
233 | |
234 // Verify that the new body text is present on the revision. | |
235 $this->drupalGet("node/" . $node->id() . "/revisions/" . $new_node_revision->getRevisionId() . "/view"); | |
236 $this->assertText($new_body, 'Revision body text is present when loading specific revision.'); | |
237 | |
238 // Verify that the non-default revision vid is greater than the default | |
239 // revision vid. | |
240 $default_revision = db_select('node', 'n') | |
241 ->fields('n', ['vid']) | |
242 ->condition('nid', $node->id()) | |
243 ->execute() | |
244 ->fetchCol(); | |
245 $default_revision_vid = $default_revision[0]; | |
246 $this->assertTrue($new_node_revision->getRevisionId() > $default_revision_vid, 'Revision vid is greater than default revision vid.'); | |
247 | |
248 // Create an 'EN' node with a revision log message. | |
249 $node = $this->drupalCreateNode(); | |
250 $node->title = 'Node title in EN'; | |
251 $node->revision_log = 'Simple revision message (EN)'; | |
252 $node->save(); | |
253 | |
254 $this->drupalGet("node/" . $node->id() . "/revisions"); | |
255 $this->assertResponse(403); | |
256 | |
257 // Create a new revision and new log message. | |
258 $node = Node::load($node->id()); | |
259 $node->body->value = 'New text (EN)'; | |
260 $node->revision_log = 'New revision message (EN)'; | |
261 $node->setNewRevision(); | |
262 $node->save(); | |
263 | |
264 // Check both revisions are shown on the node revisions overview page. | |
265 $this->drupalGet("node/" . $node->id() . "/revisions"); | |
266 $this->assertText('Simple revision message (EN)'); | |
267 $this->assertText('New revision message (EN)'); | |
268 | |
269 // Create an 'EN' node with a revision log message. | |
270 $node = $this->drupalCreateNode(); | |
271 $node->langcode = 'en'; | |
272 $node->title = 'Node title in EN'; | |
273 $node->revision_log = 'Simple revision message (EN)'; | |
274 $node->save(); | |
275 | |
276 $this->drupalGet("node/" . $node->id() . "/revisions"); | |
277 $this->assertResponse(403); | |
278 | |
279 // Add a translation in 'DE' and create a new revision and new log message. | |
280 $translation = $node->addTranslation('de'); | |
281 $translation->title->value = 'Node title in DE'; | |
282 $translation->body->value = 'New text (DE)'; | |
283 $translation->revision_log = 'New revision message (DE)'; | |
284 $translation->setNewRevision(); | |
285 $translation->save(); | |
286 | |
287 // View the revision UI in 'IT', only the original node revision is shown. | |
288 $this->drupalGet("it/node/" . $node->id() . "/revisions"); | |
289 $this->assertText('Simple revision message (EN)'); | |
290 $this->assertNoText('New revision message (DE)'); | |
291 | |
292 // View the revision UI in 'DE', only the translated node revision is shown. | |
293 $this->drupalGet("de/node/" . $node->id() . "/revisions"); | |
294 $this->assertNoText('Simple revision message (EN)'); | |
295 $this->assertText('New revision message (DE)'); | |
296 | |
297 // View the revision UI in 'EN', only the original node revision is shown. | |
298 $this->drupalGet("node/" . $node->id() . "/revisions"); | |
299 $this->assertText('Simple revision message (EN)'); | |
300 $this->assertNoText('New revision message (DE)'); | |
301 } | |
302 | |
303 /** | |
304 * Checks that revisions are correctly saved without log messages. | |
305 */ | |
306 public function testNodeRevisionWithoutLogMessage() { | |
307 $node_storage = $this->container->get('entity.manager')->getStorage('node'); | |
308 // Create a node with an initial log message. | |
309 $revision_log = $this->randomMachineName(10); | |
310 $node = $this->drupalCreateNode(['revision_log' => $revision_log]); | |
311 | |
312 // Save over the same revision and explicitly provide an empty log message | |
313 // (for example, to mimic the case of a node form submitted with no text in | |
314 // the "log message" field), and check that the original log message is | |
315 // preserved. | |
316 $new_title = $this->randomMachineName(10) . 'testNodeRevisionWithoutLogMessage1'; | |
317 | |
318 $node = clone $node; | |
319 $node->title = $new_title; | |
320 $node->revision_log = ''; | |
321 $node->setNewRevision(FALSE); | |
322 | |
323 $node->save(); | |
324 $this->drupalGet('node/' . $node->id()); | |
325 $this->assertText($new_title, 'New node title appears on the page.'); | |
326 $node_storage->resetCache([$node->id()]); | |
327 $node_revision = $node_storage->load($node->id()); | |
328 $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.'); | |
329 | |
330 // Create another node with an initial revision log message. | |
331 $node = $this->drupalCreateNode(['revision_log' => $revision_log]); | |
332 | |
333 // Save a new node revision without providing a log message, and check that | |
334 // this revision has an empty log message. | |
335 $new_title = $this->randomMachineName(10) . 'testNodeRevisionWithoutLogMessage2'; | |
336 | |
337 $node = clone $node; | |
338 $node->title = $new_title; | |
339 $node->setNewRevision(); | |
340 $node->revision_log = NULL; | |
341 | |
342 $node->save(); | |
343 $this->drupalGet('node/' . $node->id()); | |
344 $this->assertText($new_title, 'New node title appears on the page.'); | |
345 $node_storage->resetCache([$node->id()]); | |
346 $node_revision = $node_storage->load($node->id()); | |
347 $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.'); | |
348 } | |
349 | |
350 /** | |
351 * Gets server-rendered contextual links for the given contextual links IDs. | |
352 * | |
353 * @param string[] $ids | |
354 * An array of contextual link IDs. | |
355 * @param string $current_path | |
356 * The Drupal path for the page for which the contextual links are rendered. | |
357 * | |
358 * @return string | |
359 * The decoded JSON response body. | |
360 */ | |
361 protected function renderContextualLinks(array $ids, $current_path) { | |
362 $post = []; | |
363 for ($i = 0; $i < count($ids); $i++) { | |
364 $post['ids[' . $i . ']'] = $ids[$i]; | |
365 } | |
366 $response = $this->drupalPost('contextual/render', 'application/json', $post, ['query' => ['destination' => $current_path]]); | |
367 | |
368 return Json::decode($response); | |
369 } | |
370 | |
371 /** | |
372 * Tests the revision translations are correctly reverted. | |
373 */ | |
374 public function testRevisionTranslationRevert() { | |
375 // Create a node and a few revisions. | |
376 $node = $this->drupalCreateNode(['langcode' => 'en']); | |
377 | |
378 $initial_revision_id = $node->getRevisionId(); | |
379 $initial_title = $node->label(); | |
380 $this->createRevisions($node, 2); | |
381 | |
382 // Translate the node and create a few translation revisions. | |
383 $translation = $node->addTranslation('it'); | |
384 $this->createRevisions($translation, 3); | |
385 $revert_id = $node->getRevisionId(); | |
386 $translated_title = $translation->label(); | |
387 $untranslatable_string = $node->untranslatable_string_field->value; | |
388 | |
389 // Create a new revision for the default translation in-between a series of | |
390 // translation revisions. | |
391 $this->createRevisions($node, 1); | |
392 $default_translation_title = $node->label(); | |
393 | |
394 // And create a few more translation revisions. | |
395 $this->createRevisions($translation, 2); | |
396 $translation_revision_id = $translation->getRevisionId(); | |
397 | |
398 // Now revert the a translation revision preceding the last default | |
399 // translation revision, and check that the desired value was reverted but | |
400 // the default translation value was preserved. | |
401 $revert_translation_url = Url::fromRoute('node.revision_revert_translation_confirm', [ | |
402 'node' => $node->id(), | |
403 'node_revision' => $revert_id, | |
404 'langcode' => 'it', | |
405 ]); | |
406 $this->drupalPostForm($revert_translation_url, [], t('Revert')); | |
407 /** @var \Drupal\node\NodeStorage $node_storage */ | |
408 $node_storage = $this->container->get('entity.manager')->getStorage('node'); | |
409 $node_storage->resetCache(); | |
410 /** @var \Drupal\node\NodeInterface $node */ | |
411 $node = $node_storage->load($node->id()); | |
412 $this->assertTrue($node->getRevisionId() > $translation_revision_id); | |
413 $this->assertEqual($node->label(), $default_translation_title); | |
414 $this->assertEqual($node->getTranslation('it')->label(), $translated_title); | |
415 $this->assertNotEqual($node->untranslatable_string_field->value, $untranslatable_string); | |
416 | |
417 $latest_revision_id = $translation->getRevisionId(); | |
418 | |
419 // Now revert the a translation revision preceding the last default | |
420 // translation revision again, and check that the desired value was reverted | |
421 // but the default translation value was preserved. But in addition the | |
422 // untranslated field will be reverted as well. | |
423 $this->drupalPostForm($revert_translation_url, ['revert_untranslated_fields' => TRUE], t('Revert')); | |
424 $node_storage->resetCache(); | |
425 /** @var \Drupal\node\NodeInterface $node */ | |
426 $node = $node_storage->load($node->id()); | |
427 $this->assertTrue($node->getRevisionId() > $latest_revision_id); | |
428 $this->assertEqual($node->label(), $default_translation_title); | |
429 $this->assertEqual($node->getTranslation('it')->label(), $translated_title); | |
430 $this->assertEqual($node->untranslatable_string_field->value, $untranslatable_string); | |
431 | |
432 $latest_revision_id = $translation->getRevisionId(); | |
433 | |
434 // Now revert the entity revision to the initial one where the translation | |
435 // didn't exist. | |
436 $revert_url = Url::fromRoute('node.revision_revert_confirm', [ | |
437 'node' => $node->id(), | |
438 'node_revision' => $initial_revision_id, | |
439 ]); | |
440 $this->drupalPostForm($revert_url, [], t('Revert')); | |
441 $node_storage->resetCache(); | |
442 /** @var \Drupal\node\NodeInterface $node */ | |
443 $node = $node_storage->load($node->id()); | |
444 $this->assertTrue($node->getRevisionId() > $latest_revision_id); | |
445 $this->assertEqual($node->label(), $initial_title); | |
446 $this->assertFalse($node->hasTranslation('it')); | |
447 } | |
448 | |
449 /** | |
450 * Creates a series of revisions for the specified node. | |
451 * | |
452 * @param \Drupal\node\NodeInterface $node | |
453 * The node object. | |
454 * @param $count | |
455 * The number of revisions to be created. | |
456 */ | |
457 protected function createRevisions(NodeInterface $node, $count) { | |
458 for ($i = 0; $i < $count; $i++) { | |
459 $node->title = $this->randomString(); | |
460 $node->untranslatable_string_field->value = $this->randomString(); | |
461 $node->setNewRevision(TRUE); | |
462 $node->save(); | |
463 } | |
464 } | |
465 | |
466 } |