1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
|
<?php
declare(strict_types=1);
namespace Drupal\Tests\file\Functional;
use Drupal\Component\Utility\Html;
use Drupal\file\Entity\File;
/**
* Tests the 'managed_file' element type.
*
* @group file
* @todo Create a FileTestBase class and move FileFieldTestBase methods
* that aren't related to fields into it.
*/
class FileManagedFileElementTest extends FileFieldTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests the managed_file element type.
*/
public function testManagedFile(): void {
// Check that $element['#size'] is passed to the child upload element.
$this->drupalGet('file/test');
$field = $this->assertSession()->fieldExists("files[nested_file]");
$this->assertEquals(13, $field->getAttribute('size'));
// Perform the tests with all permutations of $form['#tree'],
// $element['#extended'], and $element['#multiple'].
$test_file = $this->getTestFile('text');
foreach ([0, 1] as $tree) {
foreach ([0, 1] as $extended) {
foreach ([0, 1] as $multiple) {
$path = 'file/test/' . $tree . '/' . $extended . '/' . $multiple;
$input_base_name = $tree ? 'nested_file' : 'file';
$file_field_name = $multiple ? 'files[' . $input_base_name . '][]' : 'files[' . $input_base_name . ']';
$this->drupalGet($path);
// Ensure the aria-describedby relationship works as expected.
$input_id = Html::getId('edit_' . $input_base_name);
$this->assertSession()->elementExists('css', '#' . $input_id . '--description');
$this->assertSession()->elementExists('css', '[aria-describedby="' . $input_id . '--description"]');
// Submit without a file.
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains("The file ids are .");
// Submit with a file, but with an invalid form token. Ensure the file
// was not saved.
$last_fid_prior = $this->getLastFileId();
$this->drupalGet($path);
$form_token_field = $this->assertSession()->hiddenFieldExists('form_token');
$form_token_field->setValue('invalid token');
$edit = [
$file_field_name => \Drupal::service('file_system')->realpath($test_file->getFileUri()),
];
$this->submitForm($edit, 'Save');
$this->assertSession()->pageTextContains('The form has become outdated.');
$last_fid = $this->getLastFileId();
$this->assertEquals($last_fid_prior, $last_fid, 'File was not saved when uploaded with an invalid form token.');
// Submit a new file, without using the Upload button.
$last_fid_prior = $this->getLastFileId();
$edit = [$file_field_name => \Drupal::service('file_system')->realpath($test_file->getFileUri())];
$this->drupalGet($path);
$this->submitForm($edit, 'Save');
$last_fid = $this->getLastFileId();
$this->assertGreaterThan($last_fid_prior, $last_fid, 'New file got saved.');
$this->assertSession()->pageTextContains("The file ids are $last_fid.");
// Submit no new input, but with a default file.
$this->drupalGet($path . '/' . $last_fid);
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains("The file ids are $last_fid.");
// Upload, then Submit.
$last_fid_prior = $this->getLastFileId();
$this->drupalGet($path);
$edit = [$file_field_name => \Drupal::service('file_system')->realpath($test_file->getFileUri())];
$this->submitForm($edit, 'Upload');
$last_fid = $this->getLastFileId();
$this->assertGreaterThan($last_fid_prior, $last_fid, 'New file got uploaded.');
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains("The file ids are $last_fid.");
// Remove, then Submit.
$remove_button_title = $multiple ? 'Remove selected' : 'Remove';
$remove_edit = [];
if ($multiple) {
$selected_checkbox = ($tree ? 'nested[file]' : 'file') . '[file_' . $last_fid . '][selected]';
$remove_edit = [$selected_checkbox => '1'];
}
$this->drupalGet($path . '/' . $last_fid);
$this->submitForm($remove_edit, $remove_button_title);
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains("The file ids are .");
// Upload, then Remove, then Submit.
$this->drupalGet($path);
$edit = [$file_field_name => \Drupal::service('file_system')->realpath($test_file->getFileUri())];
$this->submitForm($edit, 'Upload');
$remove_edit = [];
if ($multiple) {
$selected_checkbox = ($tree ? 'nested[file]' : 'file') . '[file_' . $this->getLastFileId() . '][selected]';
$remove_edit = [$selected_checkbox => '1'];
}
$this->submitForm($remove_edit, $remove_button_title);
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains("The file ids are .");
}
}
}
// The multiple file upload has additional conditions that need checking.
$path = 'file/test/1/1/1';
$edit = ['files[nested_file][]' => \Drupal::service('file_system')->realpath($test_file->getFileUri())];
$fid_list = [];
$this->drupalGet($path);
// Add a single file to the upload field.
$this->submitForm($edit, 'Upload');
$fid_list[] = $this->getLastFileId();
$this->assertSession()->fieldExists("nested[file][file_{$fid_list[0]}][selected]");
// Add another file to the same upload field.
$this->submitForm($edit, 'Upload');
$fid_list[] = $this->getLastFileId();
$this->assertSession()->fieldExists("nested[file][file_{$fid_list[1]}][selected]");
// Save the entire form.
$this->submitForm([], 'Save');
// Check that two files are saved into a single multiple file element.
$this->assertSession()->pageTextContains("The file ids are " . implode(',', $fid_list) . ".");
// Delete only the first file.
$edit = [
'nested[file][file_' . $fid_list[0] . '][selected]' => '1',
];
$this->drupalGet($path . '/' . implode(',', $fid_list));
$this->submitForm($edit, 'Remove selected');
// Check that the first file has been deleted but not the second.
$this->assertSession()->fieldNotExists("nested[file][file_{$fid_list[0]}][selected]");
$this->assertSession()->fieldExists("nested[file][file_{$fid_list[1]}][selected]");
}
/**
* Ensure that warning is shown if file on the field has been removed.
*/
public function testManagedFileRemoved(): void {
$this->drupalGet('file/test/1/0/1');
$test_file = $this->getTestFile('text');
$file_field_name = 'files[nested_file][]';
$edit = [$file_field_name => \Drupal::service('file_system')->realpath($test_file->getFileUri())];
$this->submitForm($edit, 'Upload');
$fid = $this->getLastFileId();
$file = \Drupal::entityTypeManager()->getStorage('file')->load($fid);
$file->delete();
$this->submitForm($edit, 'Upload');
// We expect the title 'Managed <em>file & butter</em>' which got escaped
// via a t() call before.
$this->assertSession()->responseContains('The file referenced by the Managed <em>file & butter</em> field does not exist.');
}
/**
* Tests file names have leading . removed.
*/
public function testFileNameTrim(): void {
file_put_contents('public://.leading-period.txt', $this->randomString(32));
$last_fid_prior = $this->getLastFileId();
$this->drupalGet('file/test/0/0/0');
$this->submitForm(['files[file]' => \Drupal::service('file_system')->realpath('public://.leading-period.txt')], 'Save');
$next_fid = $this->getLastFileId();
$this->assertGreaterThan($last_fid_prior, $next_fid);
$file = File::load($next_fid);
$this->assertEquals('leading-period.txt', $file->getFilename());
}
/**
* Ensure a file entity can be saved when the file does not exist on disk.
*/
public function testFileRemovedFromDisk(): void {
$this->drupalGet('file/test/1/0/1');
$test_file = $this->getTestFile('text');
$file_field_name = 'files[nested_file][]';
$edit = [$file_field_name => \Drupal::service('file_system')->realpath($test_file->getFileUri())];
$this->submitForm($edit, 'Upload');
$this->submitForm([], 'Save');
$fid = $this->getLastFileId();
/** @var \Drupal\file\FileInterface $file */
$file = $this->container->get('entity_type.manager')->getStorage('file')->load($fid);
$file->setPermanent();
$file->save();
$this->assertTrue(\Drupal::service('file_system')->delete($file->getFileUri()));
$file->save();
$this->assertTrue($file->isPermanent());
$file->delete();
}
/**
* Verify that unused permanent files can be used.
*/
public function testUnusedPermanentFileValidation(): void {
// Create a permanent file without usages.
$file = $this->getTestFile('image');
$file->setPermanent();
$file->save();
// By default, unused files are no longer marked temporary, and it must be
// allowed to reference an unused file.
$this->drupalGet('file/test/1/0/1/' . $file->id());
$this->submitForm([], 'Save');
$this->assertSession()->pageTextNotContains('The file used in the Managed file & butter field may not be referenced.');
$this->assertSession()->pageTextContains('The file ids are ' . $file->id());
// Enable marking unused files as temporary, unused permanent files must not
// be referenced now.
$this->config('file.settings')
->set('make_unused_managed_files_temporary', TRUE)
->save();
$this->drupalGet('file/test/1/0/1/' . $file->id());
$this->submitForm([], 'Save');
$this->assertSession()->pageTextContains('The file used in the Managed file & butter field may not be referenced.');
$this->assertSession()->pageTextNotContains('The file ids are ' . $file->id());
// Make the file temporary, now using it is allowed.
$file->setTemporary();
$file->save();
$this->drupalGet('file/test/1/0/1/' . $file->id());
$this->submitForm([], 'Save');
$this->assertSession()->pageTextNotContains('The file used in the Managed file & butter field may not be referenced.');
$this->assertSession()->pageTextContains('The file ids are ' . $file->id());
// Make the file permanent again and add a usage from itself, referencing is
// still allowed.
$file->setPermanent();
$file->save();
/** @var \Drupal\file\FileUsage\FileUsageInterface $file_usage */
$file_usage = \Drupal::service('file.usage');
$file_usage->add($file, 'file', 'file', $file->id());
$this->drupalGet('file/test/1/0/1/' . $file->id());
$this->submitForm([], 'Save');
$this->assertSession()->pageTextNotContains('The file used in the Managed file & butter field may not be referenced.');
$this->assertSession()->pageTextContains('The file ids are ' . $file->id());
}
}
|