Completed
Push — new-committers ( 29cb6f...bcba16 )
by Sam
12:18 queued 33s
created

UploadFieldTest   F

Complexity

Total Complexity 57

Size/Duplication

Total Lines 1021
Duplicated Lines 14.4 %

Coupling/Cohesion

Components 1
Dependencies 17
Metric Value
wmc 57
lcom 1
cbo 17
dl 147
loc 1021
rs 2.5406

38 Methods

Rating   Name   Duplication   Size   Complexity  
A getMockForm() 0 3 1
A testUploadNoRelation() 0 14 1
B testUploadHasOneRelation() 29 29 1
B testUploadHasOneRelationWithExtendedFile() 29 29 1
B testUploadHasManyRelation() 0 26 1
B testUploadManyManyRelation() 0 30 1
B testAllowedExtensions() 0 38 1
B testAllowedMaxFileNumberWithHasOne() 0 31 3
A testAllowedMaxFileNumberWithHasMany() 0 52 1
A testRemoveFromHasOne() 0 18 1
A testRemoveFromHasMany() 0 19 1
A testRemoveFromManyMany() 0 21 1
A testDeleteFromHasOne() 0 23 1
B testDeleteFromHasMany() 0 25 1
B testDeleteFromManyMany() 0 38 1
B testView() 0 26 2
B testEdit() 0 32 1
A testGetRecord() 0 18 1
B testSetItems() 0 26 1
B testGetItems() 0 27 1
B testReadonly() 24 24 1
A testDisabled() 21 21 1
A testCanUpload() 0 18 1
A testCanUploadWithPermissionCode() 0 17 1
B testCanAttachExisting() 0 25 1
A testSelect() 17 17 1
A testSelectWithDisplayFolderName() 17 17 1
B testConfigOverwriteWarningCannotRelaceFiles() 0 35 1
A testFileExists() 0 59 1
A getUploadFile() 0 15 2
B mockUploadFileIDs() 0 27 4
A mockFileUpload() 0 8 1
A mockFileExists() 0 5 1
A mockFileEditForm() 0 5 1
A mockFileEdit() 0 6 1
A mockFileDelete() 0 6 1
B setUp() 0 20 5
D tearDown() 10 29 9

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like UploadFieldTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UploadFieldTest, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package framework
4
 * @subpackage tests
5
 */
6
7
class UploadFieldTest extends FunctionalTest {
8
9
	protected static $fixture_file = 'UploadFieldTest.yml';
10
11
	protected $extraDataObjects = array('UploadFieldTest_Record');
12
13
	protected $requiredExtensions = array(
14
		'File' => array('UploadFieldTest_FileExtension')
15
	);
16
17
	/**
18
	 * Test that files can be uploaded against an object with no relation
19
	 */
20
	public function testUploadNoRelation() {
21
		$this->loginWithPermission('ADMIN');
22
23
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
0 ignored issues
show
Unused Code introduced by
$record is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
24
25
		$tmpFileName = 'testUploadBasic.txt';
26
		$response = $this->mockFileUpload('NoRelationField', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
27
		$this->assertFalse($response->isError());
28
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
29
		$uploadedFile = DataObject::get_one('File', array(
30
			'"File"."Name"' => $tmpFileName
31
		));
32
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
33
	}
34
35
	/**
36
	 * Test that an object can be uploaded against an object with a has_one relation
37
	 */
38 View Code Duplication
	public function testUploadHasOneRelation() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
39
		$this->loginWithPermission('ADMIN');
40
41
		// Unset existing has_one relation before re-uploading
42
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
43
		$record->HasOneFileID = null;
0 ignored issues
show
Documentation introduced by
The property HasOneFileID does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
44
		$record->write();
45
46
		// Firstly, ensure the file can be uploaded
47
		$tmpFileName = 'testUploadHasOneRelation.txt';
48
		$response = $this->mockFileUpload('HasOneFile', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
49
		$this->assertFalse($response->isError());
50
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
51
		$uploadedFile = DataObject::get_one('File', array(
52
			'"File"."Name"' => $tmpFileName
53
		));
54
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
55
56
		// Secondly, ensure that simply uploading an object does not save the file against the relation
57
		$record = DataObject::get_by_id($record->class, $record->ID, false);
58
		$this->assertFalse($record->HasOneFile()->exists());
59
60
		// Thirdly, test submitting the form with the encoded data
61
		$response = $this->mockUploadFileIDs('HasOneFile', array($uploadedFile->ID));
62
		$this->assertEmpty($response['errors']);
63
		$record = DataObject::get_by_id($record->class, $record->ID, false);
64
		$this->assertTrue($record->HasOneFile()->exists());
65
		$this->assertEquals($record->HasOneFile()->Name, $tmpFileName);
66
	}
67
68
	/**
69
	 * Tests that has_one relations work with subclasses of File
70
	 */
71 View Code Duplication
	public function testUploadHasOneRelationWithExtendedFile() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
72
		$this->loginWithPermission('ADMIN');
73
74
		// Unset existing has_one relation before re-uploading
75
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
76
		$record->HasOneExtendedFileID = null;
0 ignored issues
show
Documentation introduced by
The property HasOneExtendedFileID does not exist on object<DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
77
		$record->write();
78
79
		// Test that the file can be safely uploaded
80
		$tmpFileName = 'testUploadHasOneRelationWithExtendedFile.txt';
81
		$response = $this->mockFileUpload('HasOneExtendedFile', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
82
		$this->assertFalse($response->isError());
83
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
84
		$uploadedFile = DataObject::get_one('UploadFieldTest_ExtendedFile', array(
85
			'"File"."Name"' => $tmpFileName
86
		));
87
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
88
89
		// Test that the record isn't written to automatically
90
		$record = DataObject::get_by_id($record->class, $record->ID, false);
91
		$this->assertFalse($record->HasOneExtendedFile()->exists());
92
93
		// Test that saving the form writes the record
94
		$response = $this->mockUploadFileIDs('HasOneExtendedFile', array($uploadedFile->ID));
95
		$this->assertEmpty($response['errors']);
96
		$record = DataObject::get_by_id($record->class, $record->ID, false);
97
		$this->assertTrue($record->HasOneExtendedFile()->exists());
98
		$this->assertEquals($record->HasOneExtendedFile()->Name, $tmpFileName);
99
	}
100
101
102
	/**
103
	 * Test that has_many relations work with files
104
	 */
105
	public function testUploadHasManyRelation() {
106
		$this->loginWithPermission('ADMIN');
107
108
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
109
110
		// Test that uploaded files can be posted to a has_many relation
111
		$tmpFileName = 'testUploadHasManyRelation.txt';
112
		$response = $this->mockFileUpload('HasManyFiles', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
113
		$this->assertFalse($response->isError());
114
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
115
		$uploadedFile = DataObject::get_one('File', array(
116
			'"File"."Name"' => $tmpFileName
117
		));
118
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
119
120
		// Test that the record isn't written to automatically
121
		$record = DataObject::get_by_id($record->class, $record->ID, false);
122
		$this->assertEquals(2, $record->HasManyFiles()->Count()); // Existing two files should be retained
123
124
		// Test that saving the form writes the record
125
		$ids = array_merge($record->HasManyFiles()->getIDList(), array($uploadedFile->ID));
126
		$response = $this->mockUploadFileIDs('HasManyFiles', $ids);
127
		$this->assertEmpty($response['errors']);
128
		$record = DataObject::get_by_id($record->class, $record->ID, false);
129
		$this->assertEquals(3, $record->HasManyFiles()->Count()); // New record should appear here now
130
	}
131
132
	/**
133
	 * Test that many_many relationships work with files
134
	 */
135
	public function testUploadManyManyRelation() {
136
		$this->loginWithPermission('ADMIN');
137
138
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
139
		$relationCount = $record->ManyManyFiles()->Count();
140
141
		// Test that uploaded files can be posted to a many_many relation
142
		$tmpFileName = 'testUploadManyManyRelation.txt';
143
		$response = $this->mockFileUpload('ManyManyFiles', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
144
		$this->assertFalse($response->isError());
145
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
146
		$uploadedFile = DataObject::get_one('File', array(
147
			'"File"."Name"' => $tmpFileName
148
		));
149
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
150
151
		// Test that the record isn't written to automatically
152
		$record = DataObject::get_by_id($record->class, $record->ID, false);
153
		// Existing file count should be retained
154
		$this->assertEquals($relationCount, $record->ManyManyFiles()->Count());
155
156
		// Test that saving the form writes the record
157
		$ids = array_merge($record->ManyManyFiles()->getIDList(), array($uploadedFile->ID));
158
		$response = $this->mockUploadFileIDs('ManyManyFiles', $ids);
159
		$this->assertEmpty($response['errors']);
160
		$record = DataObject::get_by_id($record->class, $record->ID, false);
161
		$record->flushCache();
162
		// New record should appear here now
163
		$this->assertEquals($relationCount + 1, $record->ManyManyFiles()->Count());
164
	}
165
166
	/**
167
	 * Partially covered by {@link UploadTest->testUploadAcceptsAllowedExtension()},
168
	 * but this test additionally verifies that those constraints are actually enforced
169
	 * in this controller method.
170
	 */
171
	public function testAllowedExtensions() {
172
		$this->loginWithPermission('ADMIN');
173
174
		// Test invalid file
175
		// Relies on Upload_Validator failing to allow this extension
176
		$invalidFile = 'invalid.php';
177
		$_FILES = array('AllowedExtensionsField' => $this->getUploadFile($invalidFile));
178
		$response = $this->post(
179
			'UploadFieldTest_Controller/Form/field/AllowedExtensionsField/upload',
180
			array('AllowedExtensionsField' => $this->getUploadFile($invalidFile))
181
		);
182
		$response = json_decode($response->getBody(), true);
183
		$this->assertTrue(array_key_exists('error', $response[0]));
184
		$this->assertContains('Extension is not allowed', $response[0]['error']);
185
186
		// Test valid file
187
		$validFile = 'valid.txt';
188
		$_FILES = array('AllowedExtensionsField' => $this->getUploadFile($validFile));
189
		$response = $this->post(
190
			'UploadFieldTest_Controller/Form/field/AllowedExtensionsField/upload',
191
			array('AllowedExtensionsField' => $this->getUploadFile($validFile))
192
		);
193
		$response = json_decode($response->getBody(), true);
194
		$this->assertFalse(array_key_exists('error', $response[0]));
195
196
		// Test that setAllowedExtensions rejects extensions explicitly denied by File.allowed_extensions
197
		// Relies on File::validate failing to allow this extension
198
		$invalidFile = 'invalid.php';
199
		$_FILES = array('AllowedExtensionsField' => $this->getUploadFile($invalidFile));
200
		$response = $this->post(
201
			'UploadFieldTest_Controller/Form/field/InvalidAllowedExtensionsField/upload',
202
			array('InvalidAllowedExtensionsField' => $this->getUploadFile($invalidFile))
203
		);
204
		$response = json_decode($response->getBody(), true);
205
		$this->assertTrue(array_key_exists('error', $response[0]));
206
		$this->assertContains('Extension is not allowed', $response[0]['error']);
207
208
	}
209
210
	/**
211
	 * Test that has_one relations do not support multiple files
212
	 */
213
	public function testAllowedMaxFileNumberWithHasOne() {
214
		$this->loginWithPermission('ADMIN');
215
216
		// Get references for each file to upload
217
		$file1 = $this->objFromFixture('File', 'file1');
218
		$file2 = $this->objFromFixture('File', 'file2');
219
		$fileIDs = array($file1->ID, $file2->ID);
220
221
		// Test each of the three cases - has one with no max filel limit, has one with a limit of
222
		// one, has one with a limit of more than one (makes no sense, but should test it anyway).
223
		// Each of them should public function in the same way - attaching the first file should work, the
224
		// second should cause an error.
225
		foreach (array('HasOneFile', 'HasOneFileMaxOne', 'HasOneFileMaxTwo') as $recordName) {
226
227
			// Unset existing has_one relation before re-uploading
228
			$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
229
			$record->{"{$recordName}ID"} = null;
230
			$record->write();
231
232
			// Post form with two files for this field, should result in an error
233
			$response = $this->mockUploadFileIDs($recordName, $fileIDs);
234
			$isError = !empty($response['errors']);
235
236
			// Strictly, a has_one should not allow two files, but this is overridden
237
			// by the setAllowedMaxFileNumber(2) call
238
			$maxFiles = ($recordName === 'HasOneFileMaxTwo') ? 2 : 1;
239
240
			// Assert that the form fails if the maximum number of files is exceeded
241
			$this->assertTrue((count($fileIDs) > $maxFiles) == $isError);
242
		}
243
	}
244
245
	/**
246
	 * Test that max number of items on has_many is validated
247
	 */
248
	public function testAllowedMaxFileNumberWithHasMany() {
249
		$this->loginWithPermission('ADMIN');
250
251
		// The 'HasManyFilesMaxTwo' field has a maximum of two files able to be attached to it.
252
		// We want to add files to it until we attempt to add the third. We expect that the first
253
		// two should work and the third will fail.
254
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
255
		$record->HasManyFilesMaxTwo()->removeAll();
256
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
257
258
		// Get references for each file to upload
259
		$file1 = $this->objFromFixture('File', 'file1');
260
		$file2 = $this->objFromFixture('File', 'file2');
261
		$file3 = $this->objFromFixture('File', 'file3');
262
		$this->assertTrue($file1->exists());
263
		$this->assertTrue($file2->exists());
264
		$this->assertTrue($file3->exists());
265
266
		// Write the first element, should be okay.
267
		$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID));
268
		$this->assertEmpty($response['errors']);
269
		$this->assertCount(1, $record->HasManyFilesMaxTwo());
270
		$this->assertContains($file1->ID, $record->HasManyFilesMaxTwo()->getIDList());
271
272
273
		$record->HasManyFilesMaxTwo()->removeAll();
274
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
275
		$this->assertTrue($file1->exists());
276
		$this->assertTrue($file2->exists());
277
		$this->assertTrue($file3->exists());
278
279
280
281
		// Write the second element, should be okay.
282
		$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID));
283
		$this->assertEmpty($response['errors']);
284
		$this->assertCount(2, $record->HasManyFilesMaxTwo());
285
		$this->assertContains($file1->ID, $record->HasManyFilesMaxTwo()->getIDList());
286
		$this->assertContains($file2->ID, $record->HasManyFilesMaxTwo()->getIDList());
287
288
		$record->HasManyFilesMaxTwo()->removeAll();
289
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
290
		$this->assertTrue($file1->exists());
291
		$this->assertTrue($file2->exists());
292
		$this->assertTrue($file3->exists());
293
294
295
		// Write the third element, should result in error.
296
		$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID, $file3->ID));
297
		$this->assertNotEmpty($response['errors']);
298
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
299
	}
300
301
	/**
302
	 * Test that files can be removed from has_one relations
303
	 */
304
	public function testRemoveFromHasOne() {
305
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
306
		$file1 = $this->objFromFixture('File', 'file1');
307
308
		// Check record exists
309
		$this->assertTrue($record->HasOneFile()->exists());
310
311
		// Remove from record
312
		$response = $this->mockUploadFileIDs('HasOneFile', array());
313
		$this->assertEmpty($response['errors']);
314
315
		// Check file is removed
316
		$record = DataObject::get_by_id($record->class, $record->ID, false);
317
		$this->assertFalse($record->HasOneFile()->exists());
318
319
		// Check file object itself exists
320
		$this->assertFileExists($file1->FullPath, 'File is only detached, not deleted from filesystem');
321
	}
322
323
	/**
324
	 * Test that items can be removed from has_many
325
	 */
326
	public function testRemoveFromHasMany() {
327
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
328
		$file2 = $this->objFromFixture('File', 'file2');
0 ignored issues
show
Unused Code introduced by
$file2 is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
329
		$file3 = $this->objFromFixture('File', 'file3');
330
331
		// Check record has two files attached
332
		$this->assertEquals(array('File2', 'File3'), $record->HasManyFiles()->column('Title'));
333
334
		// Remove file 2
335
		$response = $this->mockUploadFileIDs('HasManyFiles', array($file3->ID));
336
		$this->assertEmpty($response['errors']);
337
338
		// check only file 3 is left
339
		$record = DataObject::get_by_id($record->class, $record->ID, false);
340
		$this->assertEquals(array('File3'), $record->HasManyFiles()->column('Title'));
341
342
		// Check file 2 object itself exists
343
		$this->assertFileExists($file3->FullPath, 'File is only detached, not deleted from filesystem');
344
	}
345
346
	/**
347
	 * Test that items can be removed from many_many
348
	 */
349
	public function testRemoveFromManyMany() {
350
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
351
		$file4 = $this->objFromFixture('File', 'file4');
352
		$file5 = $this->objFromFixture('File', 'file5');
353
354
		// Check that both files are currently set
355
		$this->assertContains('File4', $record->ManyManyFiles()->column('Title'));
356
		$this->assertContains('File5', $record->ManyManyFiles()->column('Title'));
357
358
		// Remove file 4
359
		$response = $this->mockUploadFileIDs('ManyManyFiles', array($file5->ID));
360
		$this->assertEmpty($response['errors']);
361
362
		// check only file 5 is left
363
		$record = DataObject::get_by_id($record->class, $record->ID, false);
364
		$this->assertNotContains('File4', $record->ManyManyFiles()->column('Title'));
365
		$this->assertContains('File5', $record->ManyManyFiles()->column('Title'));
366
367
		// check file 4 object exists
368
		$this->assertFileExists($file4->FullPath, 'File is only detached, not deleted from filesystem');
369
	}
370
371
	/**
372
	 * Test that files can be deleted from has_one and the filesystem
373
	 */
374
	public function testDeleteFromHasOne() {
375
		$this->loginWithPermission('ADMIN');
376
377
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
378
		$file1 = $this->objFromFixture('File', 'file1');
379
380
		// Check that file initially exists
381
		$this->assertTrue($record->HasOneFile()->exists());
382
		$this->assertFileExists($file1->FullPath);
383
384
		// Delete physical file and update record
385
		$response = $this->mockFileDelete('HasOneFile', $file1->ID);
386
		$this->assertFalse($response->isError());
387
		$response = $this->mockUploadFileIDs('HasOneFile', array());
388
		$this->assertEmpty($response['errors']);
389
390
		// Check that file is not set against record
391
		$record = DataObject::get_by_id($record->class, $record->ID, false);
392
		$this->assertFalse($record->HasOneFile()->exists());
393
394
		// Check that the physical file is deleted
395
		$this->assertFileNotExists($file1->FullPath, 'File is also removed from filesystem');
396
	}
397
398
	/**
399
	 * Test that files can be deleted from has_many and the filesystem
400
	 */
401
	public function testDeleteFromHasMany() {
402
		$this->loginWithPermission('ADMIN');
403
404
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
405
		$file2 = $this->objFromFixture('File', 'file2');
406
		$file3 = $this->objFromFixture('File', 'file3');
407
408
		// Check that files initially exists
409
		$this->assertEquals(array('File2', 'File3'), $record->HasManyFiles()->column('Title'));
410
		$this->assertFileExists($file2->FullPath);
411
		$this->assertFileExists($file3->FullPath);
412
413
		// Delete physical file and update record without file 2
414
		$response = $this->mockFileDelete('HasManyFiles', $file2->ID);
415
		$this->assertFalse($response->isError());
416
		$response = $this->mockUploadFileIDs('HasManyFiles', array($file3->ID));
417
		$this->assertEmpty($response['errors']);
418
419
		// Test that file is removed from record
420
		$record = DataObject::get_by_id($record->class, $record->ID, false);
421
		$this->assertEquals(array('File3'), $record->HasManyFiles()->column('Title'));
422
423
		// Test that physical file is removed
424
		$this->assertFileNotExists($file2->FullPath, 'File is also removed from filesystem');
425
	}
426
427
	/**
428
	 * Test that files can be deleted from many_many and the filesystem
429
	 */
430
	public function testDeleteFromManyMany() {
431
		$this->loginWithPermission('ADMIN');
432
433
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
434
		$file4 = $this->objFromFixture('File', 'file4');
435
		$file5 = $this->objFromFixture('File', 'file5');
436
		$fileNoDelete = $this->objFromFixture('File', 'file-nodelete');
437
438
		// Test that files initially exist
439
		$setFiles = $record->ManyManyFiles()->column('Title');
440
		$this->assertContains('File4', $setFiles);
441
		$this->assertContains('File5', $setFiles);
442
		$this->assertContains('nodelete.txt', $setFiles);
443
		$this->assertFileExists($file4->FullPath);
444
		$this->assertFileExists($file5->FullPath);
445
		$this->assertFileExists($fileNoDelete->FullPath);
446
447
		// Delete physical file and update record without file 4
448
		$response = $this->mockFileDelete('ManyManyFiles', $file4->ID);
449
		$this->assertFalse($response->isError());
450
451
		// Check file is removed from record
452
		$record = DataObject::get_by_id($record->class, $record->ID, false);
453
		$this->assertNotContains('File4', $record->ManyManyFiles()->column('Title'));
454
		$this->assertContains('File5', $record->ManyManyFiles()->column('Title'));
455
456
		// Check physical file is removed from filesystem
457
		$this->assertFileNotExists($file4->FullPath, 'File is also removed from filesystem');
458
459
		// Test record-based permissions
460
		$response = $this->mockFileDelete('ManyManyFiles', $fileNoDelete->ID);
461
		$this->assertEquals(403, $response->getStatusCode());
462
463
		// Test that folders can't be deleted
464
		$folder = $this->objFromFixture('Folder', 'folder1-subfolder1');
465
		$response = $this->mockFileDelete('ManyManyFiles', $folder->ID);
466
		$this->assertEquals(403, $response->getStatusCode());
467
	}
468
469
	/**
470
	 * Test control output html
471
	 */
472
	public function testView() {
473
		$this->loginWithPermission('ADMIN');
474
475
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
0 ignored issues
show
Unused Code introduced by
$record is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
476
		$file4 = $this->objFromFixture('File', 'file4');
477
		$file5 = $this->objFromFixture('File', 'file5');
478
		$fileNoView = $this->objFromFixture('File', 'file-noview');
479
		$fileNoEdit = $this->objFromFixture('File', 'file-noedit');
480
		$fileNoDelete = $this->objFromFixture('File', 'file-nodelete');
481
482
		$response = $this->get('UploadFieldTest_Controller');
483
		$this->assertFalse($response->isError());
484
485
		$parser = new CSSContentParser($response->getBody());
486
		$items = $parser->getBySelector(
487
			'#UploadFieldTestForm_Form_HasManyNoViewFiles_Holder .ss-uploadfield-files .ss-uploadfield-item'
488
		);
489
		$ids = array();
490
		foreach($items as $item) $ids[] = (int)$item['data-fileid'];
491
492
		$this->assertContains($file4->ID, $ids, 'Views related file');
493
		$this->assertContains($file5->ID, $ids, 'Views related file');
494
		$this->assertNotContains($fileNoView->ID, $ids, "Doesn't view files without view permissions");
495
		$this->assertContains($fileNoEdit->ID, $ids, "Views files without edit permissions");
496
		$this->assertContains($fileNoDelete->ID, $ids, "Views files without delete permissions");
497
	}
498
499
	public function testEdit() {
500
		$memberID = $this->loginWithPermission('ADMIN');
0 ignored issues
show
Unused Code introduced by
$memberID is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
501
502
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
503
		$file4 = $this->objFromFixture('File', 'file4');
504
		$fileNoEdit = $this->objFromFixture('File', 'file-noedit');
505
		$folder = $this->objFromFixture('Folder', 'folder1-subfolder1');
506
507
		$response = $this->mockFileEditForm('ManyManyFiles', $file4->ID);
508
		$this->assertFalse($response->isError());
509
510
		$response = $this->mockFileEdit('ManyManyFiles', $file4->ID, array('Title' => 'File 4 modified'));
511
		$this->assertFalse($response->isError());
512
513
		$record = DataObject::get_by_id($record->class, $record->ID, false);
0 ignored issues
show
Unused Code introduced by
$record is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
514
		$file4 = DataObject::get_by_id($file4->class, $file4->ID, false);
515
		$this->assertEquals('File 4 modified', $file4->Title);
516
517
		// Test record-based permissions
518
		$response = $this->mockFileEditForm('ManyManyFiles', $fileNoEdit->ID);
519
		$this->assertEquals(403, $response->getStatusCode());
520
521
		$response = $this->mockFileEdit('ManyManyFiles', $fileNoEdit->ID, array());
522
		$this->assertEquals(403, $response->getStatusCode());
523
524
		// Test folder permissions
525
		$response = $this->mockFileEditForm('ManyManyFiles', $folder->ID);
526
		$this->assertEquals(403, $response->getStatusCode());
527
528
		$response = $this->mockFileEdit('ManyManyFiles', $folder->ID, array());
529
		$this->assertEquals(403, $response->getStatusCode());
530
	}
531
532
	public function testGetRecord() {
533
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
534
		$form = $this->getMockForm();
535
536
		$field = UploadField::create('MyField');
537
		$field->setForm($form);
538
		$this->assertNull($field->getRecord(), 'Returns no record by default');
539
540
		$field = UploadField::create('MyField');
541
		$field->setForm($form);
542
		$form->loadDataFrom($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 533 can be null; however, Form::loadDataFrom() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
543
		$this->assertEquals($record, $field->getRecord(), 'Returns record from form if available');
544
545
		$field = UploadField::create('MyField');
546
		$field->setForm($form);
547
		$field->setRecord($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 533 can be null; however, UploadField::setRecord() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
548
		$this->assertEquals($record, $field->getRecord(), 'Returns record when set explicitly');
549
	}
550
551
	public function testSetItems() {
552
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
553
		$items = new ArrayList(array(
554
			$this->objFromFixture('File', 'file1'),
555
			$this->objFromFixture('File', 'file2')
556
		));
557
558
		// Field with no record attached
559
		$field = UploadField::create('DummyField');
560
		$field->setItems($items);
561
		$this->assertEquals(array('File1', 'File2'), $field->getItems()->column('Title'));
562
563
		// Anonymous field
564
		$field = UploadField::create('MyField');
565
		$field->setRecord($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 552 can be null; however, UploadField::setRecord() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
566
		$field->setItems($items);
567
		$this->assertEquals(array('File1', 'File2'), $field->getItems()->column('Title'));
568
569
		// Field with has_one auto-detected
570
		$field = UploadField::create('HasOneFile');
571
		$field->setRecord($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 552 can be null; however, UploadField::setRecord() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
572
		$field->setItems($items);
573
		$this->assertEquals(array('File1', 'File2'), $field->getItems()->column('Title'),
574
			'Allows overwriting of items even when relationship is detected'
575
		);
576
	}
577
578
	public function testGetItems() {
579
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
580
581
		// Anonymous field
582
		$field = UploadField::create('MyField');
583
		$field->setValue(null, $record);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
584
		$this->assertEquals(array(), $field->getItems()->column('Title'));
585
586
		// Field with has_one auto-detected
587
		$field = UploadField::create('HasOneFile');
588
		$field->setValue(null, $record);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
589
		$this->assertEquals(array('File1'), $field->getItems()->column('Title'));
590
591
		// Field with has_many auto-detected
592
		$field = UploadField::create('HasManyFiles');
593
		$field->setValue(null, $record);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
594
		$this->assertEquals(array('File2', 'File3'), $field->getItems()->column('Title'));
595
596
		// Field with many_many auto-detected
597
		$field = UploadField::create('ManyManyFiles');
598
		$field->setValue(null, $record);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
599
		$this->assertNotContains('File1',$field->getItems()->column('Title'));
600
		$this->assertNotContains('File2',$field->getItems()->column('Title'));
601
		$this->assertNotContains('File3',$field->getItems()->column('Title'));
602
		$this->assertContains('File4',$field->getItems()->column('Title'));
603
		$this->assertContains('File5',$field->getItems()->column('Title'));
604
	}
605
606 View Code Duplication
	public function testReadonly() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
607
		$this->loginWithPermission('ADMIN');
608
609
		$response = $this->get('UploadFieldTest_Controller');
610
		$this->assertFalse($response->isError());
611
612
		$parser = new CSSContentParser($response->getBody());
613
614
		$this->assertFalse(
615
			(bool)$parser->getBySelector(
616
				'#UploadFieldTestForm_Form_ReadonlyField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'
617
				),
618
			'Removes all buttons on items');
619
		$this->assertFalse(
620
			(bool)$parser->getBySelector('#UploadFieldTestForm_Form_ReadonlyField .ss-uploadfield-dropzone'),
621
			'Removes dropzone'
622
		);
623
		$this->assertFalse(
624
			(bool)$parser->getBySelector(
625
				'#UploadFieldTestForm_Form_ReadonlyField .ss-uploadfield-addfile'
626
			),
627
			'Entire "add" area'
628
		);
629
	}
630
631 View Code Duplication
	public function testDisabled() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
632
		$this->loginWithPermission('ADMIN');
633
634
		$response = $this->get('UploadFieldTest_Controller');
635
		$this->assertFalse($response->isError());
636
637
		$parser = new CSSContentParser($response->getBody());
638
		$this->assertFalse(
639
			(bool)$parser->getBySelector(
640
				'#UploadFieldTestForm_Form_DisabledField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'
641
			),
642
			'Removes all buttons on items');
643
		$this->assertFalse((bool)$parser->getBySelector(
644
			'#UploadFieldTestForm_Form_DisabledField .ss-uploadfield-dropzone'
645
			),
646
			'Removes dropzone');
647
		$this->assertFalse(
648
			(bool)$parser->getBySelector('#UploadFieldTestForm_Form_DisabledField .ss-uploadfield-addfile'),
649
			'Entire "add" area'
650
		);
651
	}
652
653
	public function testCanUpload() {
654
		$this->loginWithPermission('ADMIN');
655
		$response = $this->get('UploadFieldTest_Controller');
656
		$this->assertFalse($response->isError());
657
658
		$parser = new CSSContentParser($response->getBody());
659
		$this->assertFalse(
660
			(bool)$parser->getBySelector(
661
				'#UploadFieldTestForm_Form_CanUploadFalseField_Holder .ss-uploadfield-dropzone'
662
			),
663
			'Removes dropzone');
664
		$this->assertTrue(
665
			(bool)$parser->getBySelector(
666
				'#UploadFieldTestForm_Form_CanUploadFalseField_Holder .ss-uploadfield-fromfiles'
667
			),
668
			'Keeps "From files" button'
669
		);
670
	}
671
672
	public function testCanUploadWithPermissionCode() {
673
		$field = UploadField::create('MyField');
674
675
		$field->setCanUpload(true);
676
		$this->assertTrue($field->canUpload());
677
678
		$field->setCanUpload(false);
679
		$this->assertFalse($field->canUpload());
680
681
		$this->loginWithPermission('ADMIN');
682
683
		$field->setCanUpload(false);
684
		$this->assertFalse($field->canUpload());
685
686
		$field->setCanUpload('ADMIN');
687
		$this->assertTrue($field->canUpload());
688
	}
689
690
	public function testCanAttachExisting() {
691
		$this->loginWithPermission('ADMIN');
692
		$response = $this->get('UploadFieldTest_Controller');
693
		$this->assertFalse($response->isError());
694
695
		$parser = new CSSContentParser($response->getBody());
696
		$this->assertTrue(
697
			(bool)$parser->getBySelector(
698
				'#UploadFieldTestForm_Form_CanAttachExistingFalseField_Holder .ss-uploadfield-fromcomputer-fileinput'
699
			),
700
			'Keeps input file control'
701
		);
702
		$this->assertFalse(
703
			(bool)$parser->getBySelector(
704
				'#UploadFieldTestForm_Form_CanAttachExistingFalseField_Holder .ss-uploadfield-fromfiles'
705
			),
706
			'Removes "From files" button'
707
		);
708
709
		// Test requests to select files have the correct given permission
710
		$response2 = $this->get('UploadFieldTest_Controller/Form/field/CanAttachExistingFalseField/select');
711
		$this->assertEquals(403, $response2->getStatusCode());
712
		$response3 = $this->get('UploadFieldTest_Controller/Form/field/HasOneFile/select');
713
		$this->assertEquals(200, $response3->getStatusCode());
714
	}
715
716 View Code Duplication
	public function testSelect() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
717
		$this->loginWithPermission('ADMIN');
718
719
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
0 ignored issues
show
Unused Code introduced by
$record is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
720
		$file4 = $this->objFromFixture('File', 'file4');
721
		$fileSubfolder = $this->objFromFixture('File', 'file-subfolder');
722
723
		$response = $this->get('UploadFieldTest_Controller/Form/field/ManyManyFiles/select/');
724
		$this->assertFalse($response->isError());
725
726
		// A bit too much coupling with GridField, but a full template overload would make things too complex
727
		$parser = new CSSContentParser($response->getBody());
728
		$items = $parser->getBySelector('.ss-gridfield-item');
729
		$itemIDs = array_map(create_function('$el', 'return (int)$el["data-id"];'), $items);
730
		$this->assertContains($file4->ID, $itemIDs, 'Contains file in assigned folder');
731
		$this->assertContains($fileSubfolder->ID, $itemIDs, 'Contains file in subfolder');
732
	}
733
734 View Code Duplication
	public function testSelectWithDisplayFolderName() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
735
		$this->loginWithPermission('ADMIN');
736
737
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
0 ignored issues
show
Unused Code introduced by
$record is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
738
		$file4 = $this->objFromFixture('File', 'file4');
739
		$fileSubfolder = $this->objFromFixture('File', 'file-subfolder');
740
741
		$response = $this->get('UploadFieldTest_Controller/Form/field/HasManyDisplayFolder/select/');
742
		$this->assertFalse($response->isError());
743
744
		// A bit too much coupling with GridField, but a full template overload would make things too complex
745
		$parser = new CSSContentParser($response->getBody());
746
		$items = $parser->getBySelector('.ss-gridfield-item');
747
		$itemIDs = array_map(create_function('$el', 'return (int)$el["data-id"];'), $items);
748
		$this->assertContains($file4->ID, $itemIDs, 'Contains file in assigned folder');
749
		$this->assertNotContains($fileSubfolder->ID, $itemIDs, 'Does not contain file in subfolder');
750
	}
751
752
	/**
753
	 * Test that UploadField:overwriteWarning cannot overwrite Upload:replaceFile
754
	 */
755
	public function testConfigOverwriteWarningCannotRelaceFiles() {
756
		$this->loginWithPermission('ADMIN');
757
758
		Upload::config()->replaceFile = false;
0 ignored issues
show
Documentation introduced by
The property replaceFile does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
759
		UploadField::config()->defaultConfig = array_merge(
0 ignored issues
show
Documentation introduced by
The property defaultConfig does not exist on object<Config_ForClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
760
			UploadField::config()->defaultConfig, array('overwriteWarning' => true)
761
		);
762
763
		$tmpFileName = 'testUploadBasic.txt';
764
		$response = $this->mockFileUpload('NoRelationField', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
765
		$this->assertFalse($response->isError());
766
		$responseData = Convert::json2array($response->getBody());
767
		$this->assertFileExists(ASSETS_PATH . '/UploadFieldTest/' . $responseData[0]['name']);
768
		$uploadedFile = DataObject::get_by_id('File', (int) $responseData[0]['id']);
769
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
770
771
		$tmpFileName = 'testUploadBasic.txt';
772
		$response = $this->mockFileUpload('NoRelationField', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
773
		$this->assertFalse($response->isError());
774
		$responseData = Convert::json2array($response->getBody());
775
		$this->assertFileExists(ASSETS_PATH . '/UploadFieldTest/' . $responseData[0]['name']);
776
		$uploadedFile2 = DataObject::get_by_id('File', (int) $responseData[0]['id']);
777
		$this->assertTrue(is_object($uploadedFile2), 'The file object is created');
778
		$this->assertTrue(
779
			$uploadedFile->Filename !== $uploadedFile2->Filename,
780
			'Filename is not the same'
781
		);
782
		$this->assertTrue(
783
			$uploadedFile->ID !== $uploadedFile2->ID,
784
			'File database record is not the same'
785
		);
786
787
		$uploadedFile->delete();
788
		$uploadedFile2->delete();
789
	}
790
791
	/**
792
	 * Tests that UploadField::fileexist works
793
	 */
794
	public function testFileExists() {
795
		$this->loginWithPermission('ADMIN');
796
797
		// Check that fileexist works on subfolders
798
		$nonFile = uniqid().'.txt';
799
		$responseEmpty = $this->mockFileExists('NoRelationField', $nonFile);
800
		$responseEmptyData = json_decode($responseEmpty->getBody());
801
		$this->assertFalse($responseEmpty->isError());
802
		$this->assertFalse($responseEmptyData->exists);
803
804
		// Check that filexists works on root folder
805
		$responseRoot = $this->mockFileExists('RootFolderTest', $nonFile);
806
		$responseRootData = json_decode($responseRoot->getBody());
807
		$this->assertFalse($responseRoot->isError());
808
		$this->assertFalse($responseRootData->exists);
809
810
		// Check that uploaded files can be detected in the root
811
		$tmpFileName = 'testUploadBasic.txt';
812
		$response = $this->mockFileUpload('RootFolderTest', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
813
		$this->assertFalse($response->isError());
814
		$this->assertFileExists(ASSETS_PATH . "/$tmpFileName");
815
		$responseExists = $this->mockFileExists('RootFolderTest', $tmpFileName);
816
		$responseExistsData = json_decode($responseExists->getBody());
817
		$this->assertFalse($responseExists->isError());
818
		$this->assertTrue($responseExistsData->exists);
819
820
		// Check that uploaded files can be detected
821
		$response = $this->mockFileUpload('NoRelationField', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
822
		$this->assertFalse($response->isError());
823
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileName");
824
		$responseExists = $this->mockFileExists('NoRelationField', $tmpFileName);
825
		$responseExistsData = json_decode($responseExists->getBody());
826
		$this->assertFalse($responseExists->isError());
827
		$this->assertTrue($responseExistsData->exists);
828
829
		// Test that files with invalid characters are rewritten safely and both report exists
830
		// Check that uploaded files can be detected in the root
831
		$tmpFileName = '_test___Upload___Bad.txt';
832
		$tmpFileNameExpected = 'test-Upload-Bad.txt';
833
		$response = $this->mockFileUpload('NoRelationField', $tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
834
		$this->assertFalse($response->isError());
835
		$this->assertFileExists(ASSETS_PATH . "/UploadFieldTest/$tmpFileNameExpected");
836
		// With original file
837
		$responseExists = $this->mockFileExists('NoRelationField', $tmpFileName);
838
		$responseExistsData = json_decode($responseExists->getBody());
839
		$this->assertFalse($responseExists->isError());
840
		$this->assertTrue($responseExistsData->exists);
841
		// With rewritten file
842
		$responseExists = $this->mockFileExists('NoRelationField', $tmpFileNameExpected);
843
		$responseExistsData = json_decode($responseExists->getBody());
844
		$this->assertFalse($responseExists->isError());
845
		$this->assertTrue($responseExistsData->exists);
846
847
		// Test that attempts to navigate outside of the directory return false
848
		$responseExists = $this->mockFileExists('NoRelationField', "../../../../var/private/$tmpFileName");
849
		$responseExistsData = json_decode($responseExists->getBody());
0 ignored issues
show
Unused Code introduced by
$responseExistsData is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
850
		$this->assertTrue($responseExists->isError());
851
		$this->assertContains('File is not a valid upload', $responseExists->getBody());
852
	}
853
854
	protected function getMockForm() {
855
		return new Form(new Controller(), 'Form', new FieldList(), new FieldList());
856
	}
857
858
	/**
859
	 * @return Array Emulating an entry in the $_FILES superglobal
860
	 */
861
	protected function getUploadFile($tmpFileName = 'UploadFieldTest-testUpload.txt') {
862
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
863
		$tmpFileContent = '';
864
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
865
		file_put_contents($tmpFilePath, $tmpFileContent);
866
867
		// emulates the $_FILES array
868
		return array(
869
			'name' => array('Uploads' => array($tmpFileName)),
870
			'type' => array('Uploads' => array('text/plaintext')),
871
			'size' => array('Uploads' => array(filesize($tmpFilePath))),
872
			'tmp_name' => array('Uploads' => array($tmpFilePath)),
873
			'error' => array('Uploads' => array(UPLOAD_ERR_OK)),
874
		);
875
	}
876
877
	/**
878
	 * Simulates a form post to the test controller with the specified file IDs
879
	 *
880
	 * @param string $fileField Name of field to assign ids to
881
	 * @param array $ids list of file IDs
882
	 * @return boolean Array with key 'errors'
883
	 */
884
	protected function mockUploadFileIDs($fileField, $ids) {
885
886
		// collate file ids
887
		$files = array();
888
		foreach($ids as $id) {
889
			$files[$id] = $id;
890
		}
891
892
		$data = array(
893
			'action_submit' => 1
894
		);
895
		if($files) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $files of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
896
			// Normal post requests can't submit empty array values for fields
897
			$data[$fileField] = array('Files' => $files);
898
		}
899
900
		$form = new UploadFieldTestForm();
901
		$form->loadDataFrom($data, true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
902
		if($form->validate()) {
903
			$record = $form->getRecord();
904
			$form->saveInto($record);
905
			$record->write();
906
			return array('errors' => null);
907
		} else {
908
			return array('errors' => $form->getValidator()->getErrors());
909
		}
910
	}
911
912
	/**
913
	 * Simulates a file upload
914
	 *
915
	 * @param string $fileField Name of the field to mock upload for
916
	 * @param array $tmpFileName Name of temporary file to upload
917
	 * @return SS_HTTPResponse form response
918
	 */
919
	protected function mockFileUpload($fileField, $tmpFileName) {
920
		$upload = $this->getUploadFile($tmpFileName);
0 ignored issues
show
Documentation introduced by
$tmpFileName is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
921
		$_FILES = array($fileField => $upload);
922
		return $this->post(
923
			"UploadFieldTest_Controller/Form/field/{$fileField}/upload",
924
			array($fileField => $upload)
925
		);
926
	}
927
928
	protected function mockFileExists($fileField, $fileName) {
929
		return $this->get(
930
			"UploadFieldTest_Controller/Form/field/{$fileField}/fileexists?filename=".urlencode($fileName)
931
		);
932
	}
933
934
	/**
935
	 * Gets the edit form for the given file
936
	 *
937
	 * @param string $fileField Name of the field
938
	 * @param integer $fileID ID of the file to delete
939
	 * @return SS_HTTPResponse form response
940
	 */
941
	protected function mockFileEditForm($fileField, $fileID) {
942
		return $this->get(
943
			"UploadFieldTest_Controller/Form/field/{$fileField}/item/{$fileID}/edit"
944
		);
945
	}
946
947
	/**
948
	 * Mocks edit submissions to a file
949
	 *
950
	 * @param string $fileField Name of the field
951
	 * @param integer $fileID ID of the file to delete
952
	 * @param array $fields Fields to update
953
	 * @return SS_HTTPResponse form response
954
	 */
955
	protected function mockFileEdit($fileField, $fileID, $fields = array()) {
956
		return $this->post(
957
			"UploadFieldTest_Controller/Form/field/{$fileField}/item/{$fileID}/EditForm",
958
			$fields
959
		);
960
	}
961
962
	/**
963
	 * Simulates a physical file deletion
964
	 *
965
	 * @param string $fileField Name of the field
966
	 * @param integer $fileID ID of the file to delete
967
	 * @return SS_HTTPResponse form response
968
	 */
969
	protected function mockFileDelete($fileField, $fileID) {
970
		return $this->post(
971
			"UploadFieldTest_Controller/Form/field/{$fileField}/item/{$fileID}/delete",
972
			array()
973
		);
974
	}
975
976
	public function setUp() {
977
		Config::inst()->update('File', 'update_filesystem', false);
978
		parent::setUp();
979
980
		if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
981
982
		/* Create a test folders for each of the fixture references */
983
		$folders = Folder::get()->byIDs($this->allFixtureIDs('Folder'));
0 ignored issues
show
Documentation introduced by
$this->allFixtureIDs('Folder') is of type object<A>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
984
		foreach($folders as $folder) {
985
			if(!file_exists($folder->getFullPath())) mkdir($folder->getFullPath());
986
		}
987
988
		/* Create a test files for each of the fixture references */
989
		$files = File::get()->byIDs($this->allFixtureIDs('File'));
0 ignored issues
show
Documentation introduced by
$this->allFixtureIDs('File') is of type object<A>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
990
		foreach($files as $file) {
991
			$fh = fopen($file->getFullPath(), "w");
992
			fwrite($fh, str_repeat('x',1000000));
993
			fclose($fh);
994
		}
995
	}
996
997
	public function tearDown() {
998
		parent::tearDown();
999
1000
		/* Remove the test files that we've created */
1001
		$fileIDs = $this->allFixtureIDs('File');
1002 View Code Duplication
		foreach($fileIDs as $fileID) {
0 ignored issues
show
Bug introduced by
The expression $fileIDs of type object<A> is not traversable.
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1003
			$file = DataObject::get_by_id('File', $fileID);
1004
			if($file && file_exists(BASE_PATH."/$file->Filename")) unlink(BASE_PATH."/$file->Filename");
1005
		}
1006
1007
		/* Remove the test folders that we've crated */
1008
		$folderIDs = $this->allFixtureIDs('Folder');
1009 View Code Duplication
		foreach($folderIDs as $folderID) {
0 ignored issues
show
Bug introduced by
The expression $folderIDs of type object<A> is not traversable.
Loading history...
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1010
			$folder = DataObject::get_by_id('Folder', $folderID);
1011
			if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
1012
				Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
1013
			}
1014
		}
1015
1016
		// Remove left over folders and any files that may exist
1017
		if(file_exists(ASSETS_PATH.'/UploadFieldTest')) {
1018
			Filesystem::removeFolder(ASSETS_PATH.'/UploadFieldTest');
1019
		}
1020
1021
		// Remove file uploaded to root folder
1022
		if(file_exists(ASSETS_PATH.'/testUploadBasic.txt')) {
1023
			unlink(ASSETS_PATH.'/testUploadBasic.txt');
1024
		}
1025
	}
1026
1027
}
1028
1029
class UploadFieldTest_Record extends DataObject implements TestOnly {
1030
1031
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1032
		'Title' => 'Text',
1033
	);
1034
1035
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1036
		'HasOneFile' => 'File',
1037
		'HasOneFileMaxOne' => 'File',
1038
		'HasOneFileMaxTwo' => 'File',
1039
		'HasOneExtendedFile' => 'UploadFieldTest_ExtendedFile'
1040
	);
1041
1042
	private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1043
		'HasManyFiles' => 'File.HasManyRecord',
1044
		'HasManyFilesMaxTwo' => 'File.HasManyMaxTwoRecord',
1045
		'HasManyNoViewFiles' => 'File.HasManyNoViewRecord',
1046
		'ReadonlyField' => 'File.ReadonlyRecord'
1047
	);
1048
1049
	private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1050
		'ManyManyFiles' => 'File'
1051
	);
1052
1053
}
1054
1055
class UploadFieldTest_FileExtension extends DataExtension implements TestOnly {
1056
1057
	private static $has_one = array(
1058
		'HasManyRecord' => 'UploadFieldTest_Record',
1059
		'HasManyMaxTwoRecord' => 'UploadFieldTest_Record',
1060
		'HasManyNoViewRecord' => 'UploadFieldTest_Record',
1061
		'ReadonlyRecord' => 'UploadFieldTest_Record'
1062
	);
1063
1064
	private static $has_many = array(
1065
		'HasOneRecords' => 'UploadFieldTest_Record.HasOneFile',
1066
		'HasOneMaxOneRecords' => 'UploadFieldTest_Record.HasOneFileMaxOne',
1067
		'HasOneMaxTwoRecords' => 'UploadFieldTest_Record.HasOneFileMaxTwo',
1068
	);
1069
1070
	private static $belongs_many_many = array(
1071
		'ManyManyRecords' => 'UploadFieldTest_Record'
1072
	);
1073
1074
	public function canDelete($member = null) {
1075
		if($this->owner->Name == 'nodelete.txt') return false;
1076
	}
1077
1078
	public function canEdit($member = null) {
1079
		if($this->owner->Name == 'noedit.txt') return false;
1080
	}
1081
1082
	public function canView($member = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $member is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1083
		if($this->owner->Name == 'noview.txt') return false;
1084
	}
1085
}
1086
1087
/**
1088
 * Used for testing the create-on-upload
1089
 */
1090
class UploadFieldTest_ExtendedFile extends File implements TestOnly {
1091
1092
	private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1093
		'HasOneExtendedRecords' => 'UploadFieldTest_Record.HasOneExtendedFile'
1094
	);
1095
}
1096
1097
class UploadFieldTestForm extends Form implements TestOnly {
1098
1099
	public function getRecord() {
1100
		if(empty($this->record)) {
1101
			$this->record = DataObject::get_one('UploadFieldTest_Record', '"Title" = \'Record 1\'');
1102
		}
1103
		return $this->record;
1104
	}
1105
1106
	function __construct($controller = null, $name = 'Form') {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1107
		if(empty($controller)) {
1108
			$controller = new UploadFieldTest_Controller();
1109
		}
1110
1111
		$fieldRootFolder = UploadField::create('RootFolderTest')
1112
			->setFolderName('/');
1113
1114
		$fieldNoRelation = UploadField::create('NoRelationField')
1115
			->setFolderName('UploadFieldTest');
1116
1117
		$fieldHasOne = UploadField::create('HasOneFile')
1118
			->setFolderName('UploadFieldTest');
1119
1120
		$fieldHasOneExtendedFile = UploadField::create('HasOneExtendedFile')
1121
			->setFolderName('UploadFieldTest');
1122
1123
		$fieldHasOneMaxOne = UploadField::create('HasOneFileMaxOne')
1124
			->setFolderName('UploadFieldTest')
1125
			->setAllowedMaxFileNumber(1);
1126
1127
		$fieldHasOneMaxTwo = UploadField::create('HasOneFileMaxTwo')
1128
			->setFolderName('UploadFieldTest')
1129
			->setAllowedMaxFileNumber(2);
1130
1131
		$fieldHasMany = UploadField::create('HasManyFiles')
1132
			->setFolderName('UploadFieldTest');
1133
1134
		$fieldHasManyMaxTwo = UploadField::create('HasManyFilesMaxTwo')
1135
			->setFolderName('UploadFieldTest')
1136
			->setAllowedMaxFileNumber(2);
1137
1138
		$fieldManyMany = UploadField::create('ManyManyFiles')
1139
			->setFolderName('UploadFieldTest');
1140
1141
		$fieldHasManyNoView = UploadField::create('HasManyNoViewFiles')
1142
			->setFolderName('UploadFieldTest');
1143
1144
		$fieldHasManyDisplayFolder = UploadField::create('HasManyDisplayFolder')
1145
			->setFolderName('UploadFieldTest')
1146
			->setDisplayFolderName('UploadFieldTest');
1147
1148
		$fieldReadonly = UploadField::create('ReadonlyField')
1149
			->setFolderName('UploadFieldTest')
1150
			->performReadonlyTransformation();
1151
1152
		$fieldDisabled = UploadField::create('DisabledField')
1153
			->setFolderName('UploadFieldTest')
1154
			->performDisabledTransformation();
1155
1156
		$fieldSubfolder = UploadField::create('SubfolderField')
1157
			->setFolderName('UploadFieldTest/subfolder1');
1158
1159
		$fieldCanUploadFalse = UploadField::create('CanUploadFalseField')
1160
			->setCanUpload(false);
1161
1162
		$fieldCanAttachExisting = UploadField::create('CanAttachExistingFalseField')
1163
			->setCanAttachExisting(false);
1164
1165
		$fieldAllowedExtensions = new UploadField('AllowedExtensionsField');
1166
		$fieldAllowedExtensions->getValidator()->setAllowedExtensions(array('txt'));
1167
1168
		$fieldInvalidAllowedExtensions = new UploadField('InvalidAllowedExtensionsField');
1169
		$fieldInvalidAllowedExtensions->getValidator()->setAllowedExtensions(array('txt', 'php'));
1170
1171
		$fields = new FieldList(
1172
			$fieldRootFolder,
1173
			$fieldNoRelation,
1174
			$fieldHasOne,
1175
			$fieldHasOneMaxOne,
1176
			$fieldHasOneMaxTwo,
1177
			$fieldHasOneExtendedFile,
1178
			$fieldHasMany,
1179
			$fieldHasManyMaxTwo,
1180
			$fieldManyMany,
1181
			$fieldHasManyNoView,
1182
			$fieldHasManyDisplayFolder,
1183
			$fieldReadonly,
1184
			$fieldDisabled,
1185
			$fieldSubfolder,
1186
			$fieldCanUploadFalse,
1187
			$fieldCanAttachExisting,
1188
			$fieldAllowedExtensions,
1189
			$fieldInvalidAllowedExtensions
1190
		);
1191
		$actions = new FieldList(
1192
			new FormAction('submit')
1193
		);
1194
		$validator = new RequiredFields();
1195
1196
		parent::__construct($controller, $name, $fields, $actions, $validator);
1197
1198
		$this->loadDataFrom($this->getRecord());
1199
	}
1200
1201
	public function submit($data, Form $form) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1202
		$record = $this->getRecord();
1203
		$form->saveInto($record);
1204
		$record->write();
1205
		return json_encode($record->toMap());
1206
	}
1207
}
1208
1209
1210
class UploadFieldTest_Controller extends Controller implements TestOnly {
1211
1212
	protected $template = 'BlankPage';
1213
1214
	private static $allowed_actions = array('Form', 'index', 'submit');
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1215
1216
	public function Form() {
1217
		return new UploadFieldTestForm($this, 'Form');
1218
	}
1219
}
1220