04fbcb8767f615affe1e3517ea08e3c8b25db093
[yaffs-website] / web / core / modules / file / src / Tests / FileFieldRevisionTest.php
1 <?php
2
3 namespace Drupal\file\Tests;
4
5 use Drupal\file\Entity\File;
6
7 /**
8  * Tests creating and deleting revisions with files attached.
9  *
10  * @group file
11  */
12 class FileFieldRevisionTest extends FileFieldTestBase {
13   /**
14    * Tests creating multiple revisions of a node and managing attached files.
15    *
16    * Expected behaviors:
17    *  - Adding a new revision will make another entry in the field table, but
18    *    the original file will not be duplicated.
19    *  - Deleting a revision should not delete the original file if the file
20    *    is in use by another revision.
21    *  - When the last revision that uses a file is deleted, the original file
22    *    should be deleted also.
23    */
24   public function testRevisions() {
25     // This test expects unused managed files to be marked as a temporary file
26     // and then deleted up by file_cron().
27     $this->config('file.settings')
28       ->set('make_unused_managed_files_temporary', TRUE)
29       ->save();
30     $node_storage = $this->container->get('entity.manager')->getStorage('node');
31     $type_name = 'article';
32     $field_name = strtolower($this->randomMachineName());
33     $this->createFileField($field_name, 'node', $type_name);
34     // Create the same fields for users.
35     $this->createFileField($field_name, 'user', 'user');
36
37     $test_file = $this->getTestFile('text');
38
39     // Create a new node with the uploaded file.
40     $nid = $this->uploadNodeFile($test_file, $field_name, $type_name);
41
42     // Check that the file exists on disk and in the database.
43     $node_storage->resetCache([$nid]);
44     $node = $node_storage->load($nid);
45     $node_file_r1 = File::load($node->{$field_name}->target_id);
46     $node_vid_r1 = $node->getRevisionId();
47     $this->assertFileExists($node_file_r1, 'New file saved to disk on node creation.');
48     $this->assertFileEntryExists($node_file_r1, 'File entry exists in database on node creation.');
49     $this->assertFileIsPermanent($node_file_r1, 'File is permanent.');
50
51     // Upload another file to the same node in a new revision.
52     $this->replaceNodeFile($test_file, $field_name, $nid);
53     $node_storage->resetCache([$nid]);
54     $node = $node_storage->load($nid);
55     $node_file_r2 = File::load($node->{$field_name}->target_id);
56     $node_vid_r2 = $node->getRevisionId();
57     $this->assertFileExists($node_file_r2, 'Replacement file exists on disk after creating new revision.');
58     $this->assertFileEntryExists($node_file_r2, 'Replacement file entry exists in database after creating new revision.');
59     $this->assertFileIsPermanent($node_file_r2, 'Replacement file is permanent.');
60
61     // Check that the original file is still in place on the first revision.
62     $node = node_revision_load($node_vid_r1);
63     $current_file = File::load($node->{$field_name}->target_id);
64     $this->assertEqual($node_file_r1->id(), $current_file->id(), 'Original file still in place after replacing file in new revision.');
65     $this->assertFileExists($node_file_r1, 'Original file still in place after replacing file in new revision.');
66     $this->assertFileEntryExists($node_file_r1, 'Original file entry still in place after replacing file in new revision');
67     $this->assertFileIsPermanent($node_file_r1, 'Original file is still permanent.');
68
69     // Save a new version of the node without any changes.
70     // Check that the file is still the same as the previous revision.
71     $this->drupalPostForm('node/' . $nid . '/edit', ['revision' => '1'], t('Save'));
72     $node_storage->resetCache([$nid]);
73     $node = $node_storage->load($nid);
74     $node_file_r3 = File::load($node->{$field_name}->target_id);
75     $node_vid_r3 = $node->getRevisionId();
76     $this->assertEqual($node_file_r2->id(), $node_file_r3->id(), 'Previous revision file still in place after creating a new revision without a new file.');
77     $this->assertFileIsPermanent($node_file_r3, 'New revision file is permanent.');
78
79     // Revert to the first revision and check that the original file is active.
80     $this->drupalPostForm('node/' . $nid . '/revisions/' . $node_vid_r1 . '/revert', [], t('Revert'));
81     $node_storage->resetCache([$nid]);
82     $node = $node_storage->load($nid);
83     $node_file_r4 = File::load($node->{$field_name}->target_id);
84     $this->assertEqual($node_file_r1->id(), $node_file_r4->id(), 'Original revision file still in place after reverting to the original revision.');
85     $this->assertFileIsPermanent($node_file_r4, 'Original revision file still permanent after reverting to the original revision.');
86
87     // Delete the second revision and check that the file is kept (since it is
88     // still being used by the third revision).
89     $this->drupalPostForm('node/' . $nid . '/revisions/' . $node_vid_r2 . '/delete', [], t('Delete'));
90     $this->assertFileExists($node_file_r3, 'Second file is still available after deleting second revision, since it is being used by the third revision.');
91     $this->assertFileEntryExists($node_file_r3, 'Second file entry is still available after deleting second revision, since it is being used by the third revision.');
92     $this->assertFileIsPermanent($node_file_r3, 'Second file entry is still permanent after deleting second revision, since it is being used by the third revision.');
93
94     // Attach the second file to a user.
95     $user = $this->drupalCreateUser();
96     $user->$field_name->target_id = $node_file_r3->id();
97     $user->$field_name->display = 1;
98     $user->save();
99     $this->drupalGet('user/' . $user->id() . '/edit');
100
101     // Delete the third revision and check that the file is not deleted yet.
102     $this->drupalPostForm('node/' . $nid . '/revisions/' . $node_vid_r3 . '/delete', [], t('Delete'));
103     $this->assertFileExists($node_file_r3, 'Second file is still available after deleting third revision, since it is being used by the user.');
104     $this->assertFileEntryExists($node_file_r3, 'Second file entry is still available after deleting third revision, since it is being used by the user.');
105     $this->assertFileIsPermanent($node_file_r3, 'Second file entry is still permanent after deleting third revision, since it is being used by the user.');
106
107     // Delete the user and check that the file is also deleted.
108     $user->delete();
109     // TODO: This seems like a bug in File API. Clearing the stat cache should
110     // not be necessary here. The file really is deleted, but stream wrappers
111     // doesn't seem to think so unless we clear the PHP file stat() cache.
112     clearstatcache($node_file_r1->getFileUri());
113     clearstatcache($node_file_r2->getFileUri());
114     clearstatcache($node_file_r3->getFileUri());
115     clearstatcache($node_file_r4->getFileUri());
116
117     // Call file_cron() to clean up the file. Make sure the changed timestamp
118     // of the file is older than the system.file.temporary_maximum_age
119     // configuration value.
120     db_update('file_managed')
121       ->fields([
122         'changed' => REQUEST_TIME - ($this->config('system.file')->get('temporary_maximum_age') + 1),
123       ])
124       ->condition('fid', $node_file_r3->id())
125       ->execute();
126     \Drupal::service('cron')->run();
127
128     $this->assertFileNotExists($node_file_r3, 'Second file is now deleted after deleting third revision, since it is no longer being used by any other nodes.');
129     $this->assertFileEntryNotExists($node_file_r3, 'Second file entry is now deleted after deleting third revision, since it is no longer being used by any other nodes.');
130
131     // Delete the entire node and check that the original file is deleted.
132     $this->drupalPostForm('node/' . $nid . '/delete', [], t('Delete'));
133     // Call file_cron() to clean up the file. Make sure the changed timestamp
134     // of the file is older than the system.file.temporary_maximum_age
135     // configuration value.
136     db_update('file_managed')
137       ->fields([
138         'changed' => REQUEST_TIME - ($this->config('system.file')->get('temporary_maximum_age') + 1),
139       ])
140       ->condition('fid', $node_file_r1->id())
141       ->execute();
142     \Drupal::service('cron')->run();
143     $this->assertFileNotExists($node_file_r1, 'Original file is deleted after deleting the entire node with two revisions remaining.');
144     $this->assertFileEntryNotExists($node_file_r1, 'Original file entry is deleted after deleting the entire node with two revisions remaining.');
145   }
146
147 }