Completed
Push — namespace-model ( 9b3f38...c67c40 )
by Sam
16:21 queued 05:15
created

UploadFieldTest::testUploadManyManyRelation()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 28
rs 8.8571
cc 1
eloc 18
nc 1
nop 0
1
<?php
2
3
/**
4
 * @package framework
5
 * @subpackage tests
6
 */
7
8
use SilverStripe\Model\DataObject;
9
use SilverStripe\Model\ArrayList;
10
use SilverStripe\Model\DataExtension;
11
class UploadFieldTest extends FunctionalTest {
12
13
	protected static $fixture_file = 'UploadFieldTest.yml';
14
15
	protected $extraDataObjects = array('UploadFieldTest_Record');
16
17
	protected $requiredExtensions = array(
18
		'File' => array('UploadFieldTest_FileExtension')
19
	);
20
21
	protected $oldReadingMode = null;
22
23 View Code Duplication
	public function setUp() {
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...
24
		parent::setUp();
25
26
		$this->loginWithPermission('ADMIN');
27
28
		// Save versioned state
29
		$this->oldReadingMode = Versioned::get_reading_mode();
30
		Versioned::set_stage(Versioned::DRAFT);
31
32
		// Set backend root to /UploadFieldTest
33
		AssetStoreTest_SpyStore::activate('UploadFieldTest');
34
35
		// Create a test folders for each of the fixture references
36
		foreach(Folder::get() as $folder) {
37
			$path = AssetStoreTest_SpyStore::getLocalPath($folder);
38
			Filesystem::makeFolder($path);
39
		}
40
41
		// Create a test files for each of the fixture references
42
		$files = File::get()->exclude('ClassName', 'Folder');
43
		foreach($files as $file) {
44
			$path = AssetStoreTest_SpyStore::getLocalPath($file);
45
			Filesystem::makeFolder(dirname($path));
46
			$fh = fopen($path, "w+");
47
			fwrite($fh, str_repeat('x', 1000000));
48
			fclose($fh);
49
		}
50
	}
51
52
	public function tearDown() {
53
		AssetStoreTest_SpyStore::reset();
54
		if($this->oldReadingMode) {
55
			Versioned::set_reading_mode($this->oldReadingMode);
56
		}
57
		parent::tearDown();
58
	}
59
60
	/**
61
	 * Test that files can be uploaded against an object with no relation
62
	 */
63
	public function testUploadNoRelation() {
64
		$tmpFileName = 'testUploadBasic.txt';
65
		$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...
66
		$this->assertFalse($response->isError());
67
		$uploadedFile = DataObject::get_one('File', array(
68
			'"File"."Name"' => $tmpFileName
69
		));
70
71
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile));
0 ignored issues
show
Documentation introduced by
$uploadedFile is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
72
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
73
	}
74
75
	/**
76
	 * Test that an object can be uploaded against an object with a has_one relation
77
	 */
78 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...
79
		// Unset existing has_one relation before re-uploading
80
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
81
		$record->HasOneFileID = null;
0 ignored issues
show
Documentation introduced by
The property HasOneFileID does not exist on object<SilverStripe\Model\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...
82
		$record->write();
83
84
		// Firstly, ensure the file can be uploaded
85
		$tmpFileName = 'testUploadHasOneRelation.txt';
86
		$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...
87
		$this->assertFalse($response->isError());
88
		$uploadedFile = DataObject::get_one('File', array(
89
			'"File"."Name"' => $tmpFileName
90
		));
91
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
92
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile));
0 ignored issues
show
Documentation introduced by
$uploadedFile is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
93
94
		// Secondly, ensure that simply uploading an object does not save the file against the relation
95
		$record = DataObject::get_by_id($record->class, $record->ID, false);
96
		$this->assertFalse($record->HasOneFile()->exists());
97
98
		// Thirdly, test submitting the form with the encoded data
99
		$response = $this->mockUploadFileIDs('HasOneFile', array($uploadedFile->ID));
100
		$this->assertEmpty($response['errors']);
101
		$record = DataObject::get_by_id($record->class, $record->ID, false);
102
		$this->assertTrue($record->HasOneFile()->exists());
103
		$this->assertEquals($record->HasOneFile()->Name, $tmpFileName);
104
	}
105
106
	/**
107
	 * Tests that has_one relations work with subclasses of File
108
	 */
109 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...
110
		// Unset existing has_one relation before re-uploading
111
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
112
		$record->HasOneExtendedFileID = null;
0 ignored issues
show
Documentation introduced by
The property HasOneExtendedFileID does not exist on object<SilverStripe\Model\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...
113
		$record->write();
114
115
		// Test that the file can be safely uploaded
116
		$tmpFileName = 'testUploadHasOneRelationWithExtendedFile.txt';
117
		$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...
118
		$this->assertFalse($response->isError());
119
		$uploadedFile = DataObject::get_one('UploadFieldTest_ExtendedFile', array(
120
			'"File"."Name"' => $tmpFileName
121
		));
122
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
123
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile));
0 ignored issues
show
Documentation introduced by
$uploadedFile is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
124
125
		// Test that the record isn't written to automatically
126
		$record = DataObject::get_by_id($record->class, $record->ID, false);
127
		$this->assertFalse($record->HasOneExtendedFile()->exists());
128
129
		// Test that saving the form writes the record
130
		$response = $this->mockUploadFileIDs('HasOneExtendedFile', array($uploadedFile->ID));
131
		$this->assertEmpty($response['errors']);
132
		$record = DataObject::get_by_id($record->class, $record->ID, false);
133
		$this->assertTrue($record->HasOneExtendedFile()->exists());
134
		$this->assertEquals($record->HasOneExtendedFile()->Name, $tmpFileName);
135
	}
136
137
138
	/**
139
	 * Test that has_many relations work with files
140
	 */
141
	public function testUploadHasManyRelation() {
142
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
143
144
		// Test that uploaded files can be posted to a has_many relation
145
		$tmpFileName = 'testUploadHasManyRelation.txt';
146
		$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...
147
		$this->assertFalse($response->isError());
148
		$uploadedFile = DataObject::get_one('File', array(
149
			'"File"."Name"' => $tmpFileName
150
		));
151
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
152
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile));
0 ignored issues
show
Documentation introduced by
$uploadedFile is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
153
154
		// Test that the record isn't written to automatically
155
		$record = DataObject::get_by_id($record->class, $record->ID, false);
156
		$this->assertEquals(2, $record->HasManyFiles()->Count()); // Existing two files should be retained
157
158
		// Test that saving the form writes the record
159
		$ids = array_merge($record->HasManyFiles()->getIDList(), array($uploadedFile->ID));
160
		$response = $this->mockUploadFileIDs('HasManyFiles', $ids);
161
		$this->assertEmpty($response['errors']);
162
		$record = DataObject::get_by_id($record->class, $record->ID, false);
163
		$this->assertEquals(3, $record->HasManyFiles()->Count()); // New record should appear here now
164
	}
165
166
	/**
167
	 * Test that many_many relationships work with files
168
	 */
169
	public function testUploadManyManyRelation() {
170
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
171
		$relationCount = $record->ManyManyFiles()->Count();
172
173
		// Test that uploaded files can be posted to a many_many relation
174
		$tmpFileName = 'testUploadManyManyRelation.txt';
175
		$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...
176
		$this->assertFalse($response->isError());
177
		$uploadedFile = DataObject::get_one('File', array(
178
			'"File"."Name"' => $tmpFileName
179
		));
180
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
181
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile));
0 ignored issues
show
Documentation introduced by
$uploadedFile is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
182
183
		// Test that the record isn't written to automatically
184
		$record = DataObject::get_by_id($record->class, $record->ID, false);
185
		// Existing file count should be retained
186
		$this->assertEquals($relationCount, $record->ManyManyFiles()->Count());
187
188
		// Test that saving the form writes the record
189
		$ids = array_merge($record->ManyManyFiles()->getIDList(), array($uploadedFile->ID));
190
		$response = $this->mockUploadFileIDs('ManyManyFiles', $ids);
191
		$this->assertEmpty($response['errors']);
192
		$record = DataObject::get_by_id($record->class, $record->ID, false);
193
		$record->flushCache();
194
		// New record should appear here now
195
		$this->assertEquals($relationCount + 1, $record->ManyManyFiles()->Count());
196
	}
197
198
	/**
199
	 * Partially covered by {@link UploadTest->testUploadAcceptsAllowedExtension()},
200
	 * but this test additionally verifies that those constraints are actually enforced
201
	 * in this controller method.
202
	 */
203
	public function testAllowedExtensions() {
204
		// Test invalid file
205
		// Relies on Upload_Validator failing to allow this extension
206
		$invalidFile = 'invalid.php';
207
		$_FILES = array('AllowedExtensionsField' => $this->getUploadFile($invalidFile));
208
		$response = $this->post(
209
			'UploadFieldTest_Controller/Form/field/AllowedExtensionsField/upload',
210
			array('AllowedExtensionsField' => $this->getUploadFile($invalidFile))
211
		);
212
		$response = json_decode($response->getBody(), true);
213
		$this->assertTrue(array_key_exists('error', $response[0]));
214
		$this->assertContains('Extension is not allowed', $response[0]['error']);
215
216
		// Test valid file
217
		$validFile = 'valid.txt';
218
		$_FILES = array('AllowedExtensionsField' => $this->getUploadFile($validFile));
219
		$response = $this->post(
220
			'UploadFieldTest_Controller/Form/field/AllowedExtensionsField/upload',
221
			array('AllowedExtensionsField' => $this->getUploadFile($validFile))
222
		);
223
		$response = json_decode($response->getBody(), true);
224
		$this->assertFalse(array_key_exists('error', $response[0]));
225
226
		// Test that setAllowedExtensions rejects extensions explicitly denied by File.allowed_extensions
227
		// Relies on File::validate failing to allow this extension
228
		$invalidFile = 'invalid.php';
229
		$_FILES = array('AllowedExtensionsField' => $this->getUploadFile($invalidFile));
230
		$response = $this->post(
231
			'UploadFieldTest_Controller/Form/field/InvalidAllowedExtensionsField/upload',
232
			array('InvalidAllowedExtensionsField' => $this->getUploadFile($invalidFile))
233
		);
234
		$response = json_decode($response->getBody(), true);
235
		$this->assertTrue(array_key_exists('error', $response[0]));
236
		$this->assertContains('Extension is not allowed', $response[0]['error']);
237
238
	}
239
240
	/**
241
	 * Test that has_one relations do not support multiple files
242
	 */
243
	public function testAllowedMaxFileNumberWithHasOne() {
244
		// Get references for each file to upload
245
		$file1 = $this->objFromFixture('File', 'file1');
246
		$file2 = $this->objFromFixture('File', 'file2');
247
		$fileIDs = array($file1->ID, $file2->ID);
248
249
		// Test each of the three cases - has one with no max filel limit, has one with a limit of
250
		// one, has one with a limit of more than one (makes no sense, but should test it anyway).
251
		// Each of them should public function in the same way - attaching the first file should work, the
252
		// second should cause an error.
253
		foreach (array('HasOneFile', 'HasOneFileMaxOne', 'HasOneFileMaxTwo') as $recordName) {
254
255
			// Unset existing has_one relation before re-uploading
256
			$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
257
			$record->{"{$recordName}ID"} = null;
258
			$record->write();
259
260
			// Post form with two files for this field, should result in an error
261
			$response = $this->mockUploadFileIDs($recordName, $fileIDs);
262
			$isError = !empty($response['errors']);
263
264
			// Strictly, a has_one should not allow two files, but this is overridden
265
			// by the setAllowedMaxFileNumber(2) call
266
			$maxFiles = ($recordName === 'HasOneFileMaxTwo') ? 2 : 1;
267
268
			// Assert that the form fails if the maximum number of files is exceeded
269
			$this->assertTrue((count($fileIDs) > $maxFiles) == $isError);
270
		}
271
	}
272
273
	/**
274
	 * Test that max number of items on has_many is validated
275
	 */
276
	public function testAllowedMaxFileNumberWithHasMany() {
277
		// The 'HasManyFilesMaxTwo' field has a maximum of two files able to be attached to it.
278
		// We want to add files to it until we attempt to add the third. We expect that the first
279
		// two should work and the third will fail.
280
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
281
		$record->HasManyFilesMaxTwo()->removeAll();
282
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
283
284
		// Get references for each file to upload
285
		$file1 = $this->objFromFixture('File', 'file1');
286
		$file2 = $this->objFromFixture('File', 'file2');
287
		$file3 = $this->objFromFixture('File', 'file3');
288
		$this->assertTrue($file1->exists());
289
		$this->assertTrue($file2->exists());
290
		$this->assertTrue($file3->exists());
291
292
		// Write the first element, should be okay.
293
		$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID));
294
		$this->assertEmpty($response['errors']);
295
		$this->assertCount(1, $record->HasManyFilesMaxTwo());
296
		$this->assertContains($file1->ID, $record->HasManyFilesMaxTwo()->getIDList());
297
298
299
		$record->HasManyFilesMaxTwo()->removeAll();
300
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
301
		$this->assertTrue($file1->exists());
302
		$this->assertTrue($file2->exists());
303
		$this->assertTrue($file3->exists());
304
305
306
307
		// Write the second element, should be okay.
308
		$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID));
309
		$this->assertEmpty($response['errors']);
310
		$this->assertCount(2, $record->HasManyFilesMaxTwo());
311
		$this->assertContains($file1->ID, $record->HasManyFilesMaxTwo()->getIDList());
312
		$this->assertContains($file2->ID, $record->HasManyFilesMaxTwo()->getIDList());
313
314
		$record->HasManyFilesMaxTwo()->removeAll();
315
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
316
		$this->assertTrue($file1->exists());
317
		$this->assertTrue($file2->exists());
318
		$this->assertTrue($file3->exists());
319
320
321
		// Write the third element, should result in error.
322
		$response = $this->mockUploadFileIDs('HasManyFilesMaxTwo', array($file1->ID, $file2->ID, $file3->ID));
323
		$this->assertNotEmpty($response['errors']);
324
		$this->assertCount(0, $record->HasManyFilesMaxTwo());
325
	}
326
327
	/**
328
	 * Test that files can be removed from has_one relations
329
	 */
330
	public function testRemoveFromHasOne() {
331
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
332
		$file1 = $this->objFromFixture('File', 'file1');
333
334
		// Check record exists
335
		$this->assertTrue($record->HasOneFile()->exists());
336
337
		// Remove from record
338
		$response = $this->mockUploadFileIDs('HasOneFile', array());
339
		$this->assertEmpty($response['errors']);
340
341
		// Check file is removed
342
		$record = DataObject::get_by_id($record->class, $record->ID, false);
343
		$this->assertFalse($record->HasOneFile()->exists());
344
345
		// Check file object itself exists
346
		$this->assertFileExists(
347
			AssetStoreTest_SpyStore::getLocalPath($file1),
0 ignored issues
show
Documentation introduced by
$file1 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
348
			'File is only detached, not deleted from filesystem'
349
		);
350
	}
351
352
	/**
353
	 * Test that items can be removed from has_many
354
	 */
355
	public function testRemoveFromHasMany() {
356
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
357
		$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...
358
		$file3 = $this->objFromFixture('File', 'file3');
359
360
		// Check record has two files attached
361
		$this->assertEquals(array('File2', 'File3'), $record->HasManyFiles()->column('Title'));
362
363
		// Remove file 2
364
		$response = $this->mockUploadFileIDs('HasManyFiles', array($file3->ID));
365
		$this->assertEmpty($response['errors']);
366
367
		// check only file 3 is left
368
		$record = DataObject::get_by_id($record->class, $record->ID, false);
369
		$this->assertEquals(array('File3'), $record->HasManyFiles()->column('Title'));
370
371
		// Check file 2 object itself exists
372
		$this->assertFileExists(
373
			AssetStoreTest_SpyStore::getLocalPath($file3),
0 ignored issues
show
Documentation introduced by
$file3 is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
374
			'File is only detached, not deleted from filesystem'
375
		);
376
	}
377
378
	/**
379
	 * Test that items can be removed from many_many
380
	 */
381
	public function testRemoveFromManyMany() {
382
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
383
		$file4 = $this->objFromFixture('File', 'file4');
384
		$file5 = $this->objFromFixture('File', 'file5');
385
386
		// Check that both files are currently set
387
		$this->assertContains('File4', $record->ManyManyFiles()->column('Title'));
388
		$this->assertContains('File5', $record->ManyManyFiles()->column('Title'));
389
390
		// Remove file 4
391
		$response = $this->mockUploadFileIDs('ManyManyFiles', array($file5->ID));
392
		$this->assertEmpty($response['errors']);
393
394
		// check only file 5 is left
395
		$record = DataObject::get_by_id($record->class, $record->ID, false);
396
		$this->assertNotContains('File4', $record->ManyManyFiles()->column('Title'));
397
		$this->assertContains('File5', $record->ManyManyFiles()->column('Title'));
398
399
		// check file 4 object exists
400
		$this->assertFileExists(
401
			AssetStoreTest_SpyStore::getLocalPath($file4),
0 ignored issues
show
Documentation introduced by
$file4 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
402
			'File is only detached, not deleted from filesystem'
403
		);
404
	}
405
406
	/**
407
	 * Test that files can be deleted from has_one
408
	 */
409
	public function testDeleteFromHasOne() {
410
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
411
		$file1 = $this->objFromFixture('File', 'file1');
412
413
		// Check that file initially exists
414
		$this->assertTrue($record->HasOneFile()->exists());
415
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($file1));
0 ignored issues
show
Documentation introduced by
$file1 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
416
417
		// Delete file and update record
418
		$response = $this->mockFileDelete('HasOneFile', $file1->ID);
419
		$this->assertFalse($response->isError());
420
		$response = $this->mockUploadFileIDs('HasOneFile', array());
421
		$this->assertEmpty($response['errors']);
422
423
		// Check that file is not set against record
424
		$record = DataObject::get_by_id($record->class, $record->ID, false);
425
		$this->assertFalse($record->HasOneFile()->exists());
426
	}
427
428
	/**
429
	 * Test that files can be deleted from has_many
430
	 */
431
	public function testDeleteFromHasMany() {
432
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
433
		$file2 = $this->objFromFixture('File', 'file2');
434
		$file3 = $this->objFromFixture('File', 'file3');
435
436
		// Check that files initially exists
437
		$this->assertEquals(array('File2', 'File3'), $record->HasManyFiles()->column('Title'));
438
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($file2));
0 ignored issues
show
Documentation introduced by
$file2 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
439
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($file3));
0 ignored issues
show
Documentation introduced by
$file3 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
440
441
		// Delete dataobject file and update record without file 2
442
		$response = $this->mockFileDelete('HasManyFiles', $file2->ID);
443
		$this->assertFalse($response->isError());
444
		$response = $this->mockUploadFileIDs('HasManyFiles', array($file3->ID));
445
		$this->assertEmpty($response['errors']);
446
447
		// Test that file is removed from record
448
		$record = DataObject::get_by_id($record->class, $record->ID, false);
449
		$this->assertEquals(array('File3'), $record->HasManyFiles()->column('Title'));
450
	}
451
452
	/**
453
	 * Test that files can be deleted from many_many and the filesystem
454
	 */
455
	public function testDeleteFromManyMany() {
456
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
457
		$file4 = $this->objFromFixture('File', 'file4');
458
		$file5 = $this->objFromFixture('File', 'file5');
459
		$fileNoDelete = $this->objFromFixture('File', 'file-nodelete');
460
461
		// Test that files initially exist
462
		$setFiles = $record->ManyManyFiles()->column('Title');
463
		$this->assertContains('File4', $setFiles);
464
		$this->assertContains('File5', $setFiles);
465
		$this->assertContains('nodelete.txt', $setFiles);
466
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($file4));
0 ignored issues
show
Documentation introduced by
$file4 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
467
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($file5));
0 ignored issues
show
Documentation introduced by
$file5 is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
468
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($fileNoDelete));
0 ignored issues
show
Documentation introduced by
$fileNoDelete is of type object<SilverStripe\Model\DataObject>|null, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
469
470
		// Delete physical file and update record without file 4
471
		$response = $this->mockFileDelete('ManyManyFiles', $file4->ID);
472
		$this->assertFalse($response->isError());
473
474
		// Check file is removed from record
475
		$record = DataObject::get_by_id($record->class, $record->ID, false);
476
		$this->assertNotContains('File4', $record->ManyManyFiles()->column('Title'));
477
		$this->assertContains('File5', $record->ManyManyFiles()->column('Title'));
478
479
		// Test record-based permissions
480
		$response = $this->mockFileDelete('ManyManyFiles', $fileNoDelete->ID);
481
		$this->assertEquals(403, $response->getStatusCode());
482
483
		// Test that folders can't be deleted
484
		$folder = $this->objFromFixture('Folder', 'folder1-subfolder1');
485
		$response = $this->mockFileDelete('ManyManyFiles', $folder->ID);
486
		$this->assertEquals(403, $response->getStatusCode());
487
	}
488
489
	/**
490
	 * Test control output html
491
	 */
492
	public function testView() {
493
		$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...
494
		$file4 = $this->objFromFixture('File', 'file4');
495
		$file5 = $this->objFromFixture('File', 'file5');
496
		$fileNoView = $this->objFromFixture('File', 'file-noview');
497
		$fileNoEdit = $this->objFromFixture('File', 'file-noedit');
498
		$fileNoDelete = $this->objFromFixture('File', 'file-nodelete');
499
500
		$response = $this->get('UploadFieldTest_Controller');
501
		$this->assertFalse($response->isError());
502
503
		$parser = new CSSContentParser($response->getBody());
504
		$items = $parser->getBySelector(
505
			'#UploadFieldTestForm_Form_HasManyNoViewFiles_Holder .ss-uploadfield-files .ss-uploadfield-item'
506
		);
507
		$ids = array();
508
		foreach($items as $item) $ids[] = (int)$item['data-fileid'];
509
510
		$this->assertContains($file4->ID, $ids, 'Views related file');
511
		$this->assertContains($file5->ID, $ids, 'Views related file');
512
		$this->assertNotContains($fileNoView->ID, $ids, "Doesn't view files without view permissions");
513
		$this->assertContains($fileNoEdit->ID, $ids, "Views files without edit permissions");
514
		$this->assertContains($fileNoDelete->ID, $ids, "Views files without delete permissions");
515
	}
516
517
	public function testEdit() {
518
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
519
		$file4 = $this->objFromFixture('File', 'file4');
520
		$fileNoEdit = $this->objFromFixture('File', 'file-noedit');
521
		$folder = $this->objFromFixture('Folder', 'folder1-subfolder1');
522
523
		$response = $this->mockFileEditForm('ManyManyFiles', $file4->ID);
524
		$this->assertFalse($response->isError());
525
526
		$response = $this->mockFileEdit('ManyManyFiles', $file4->ID, array('Title' => 'File 4 modified'));
527
		$this->assertFalse($response->isError());
528
529
		$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...
530
		$file4 = DataObject::get_by_id($file4->class, $file4->ID, false);
531
		$this->assertEquals('File 4 modified', $file4->Title);
532
533
		// Test record-based permissions
534
		$response = $this->mockFileEditForm('ManyManyFiles', $fileNoEdit->ID);
535
		$this->assertEquals(403, $response->getStatusCode());
536
537
		$response = $this->mockFileEdit('ManyManyFiles', $fileNoEdit->ID, array());
538
		$this->assertEquals(403, $response->getStatusCode());
539
540
		// Test folder permissions
541
		$response = $this->mockFileEditForm('ManyManyFiles', $folder->ID);
542
		$this->assertEquals(403, $response->getStatusCode());
543
544
		$response = $this->mockFileEdit('ManyManyFiles', $folder->ID, array());
545
		$this->assertEquals(403, $response->getStatusCode());
546
	}
547
548 View Code Duplication
	public function testGetRecord() {
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...
549
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
550
		$form = $this->getMockForm();
551
552
		$field = UploadField::create('MyField');
553
		$field->setForm($form);
554
		$this->assertNull($field->getRecord(), 'Returns no record by default');
555
556
		$field = UploadField::create('MyField');
557
		$field->setForm($form);
558
		$form->loadDataFrom($record);
0 ignored issues
show
Documentation introduced by
$record is of type object<SilverStripe\Model\DataObject>|null, but the function expects a array|object<DataObject>.

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...
559
		$this->assertEquals($record, $field->getRecord(), 'Returns record from form if available');
560
561
		$field = UploadField::create('MyField');
562
		$field->setForm($form);
563
		$field->setRecord($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 549 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...
564
		$this->assertEquals($record, $field->getRecord(), 'Returns record when set explicitly');
565
	}
566
567
	public function testSetItems() {
568
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
569
		$items = new ArrayList(array(
570
			$this->objFromFixture('File', 'file1'),
571
			$this->objFromFixture('File', 'file2')
572
		));
573
574
		// Field with no record attached
575
		$field = UploadField::create('DummyField');
576
		$field->setItems($items);
577
		$this->assertEquals(array('File1', 'File2'), $field->getItems()->column('Title'));
578
579
		// Anonymous field
580
		$field = UploadField::create('MyField');
581
		$field->setRecord($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 568 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...
582
		$field->setItems($items);
583
		$this->assertEquals(array('File1', 'File2'), $field->getItems()->column('Title'));
584
585
		// Field with has_one auto-detected
586
		$field = UploadField::create('HasOneFile');
587
		$field->setRecord($record);
0 ignored issues
show
Bug introduced by
It seems like $record defined by $this->objFromFixture('U...est_Record', 'record1') on line 568 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...
588
		$field->setItems($items);
589
		$this->assertEquals(array('File1', 'File2'), $field->getItems()->column('Title'),
590
			'Allows overwriting of items even when relationship is detected'
591
		);
592
	}
593
594
	public function testGetItems() {
595
		$record = $this->objFromFixture('UploadFieldTest_Record', 'record1');
596
597
		// Anonymous field
598
		$field = UploadField::create('MyField');
599
		$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...
600
		$this->assertEquals(array(), $field->getItems()->column('Title'));
601
602
		// Field with has_one auto-detected
603
		$field = UploadField::create('HasOneFile');
604
		$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...
605
		$this->assertEquals(array('File1'), $field->getItems()->column('Title'));
606
607
		// Field with has_many auto-detected
608
		$field = UploadField::create('HasManyFiles');
609
		$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...
610
		$this->assertEquals(array('File2', 'File3'), $field->getItems()->column('Title'));
611
612
		// Field with many_many auto-detected
613
		$field = UploadField::create('ManyManyFiles');
614
		$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...
615
		$this->assertNotContains('File1',$field->getItems()->column('Title'));
616
		$this->assertNotContains('File2',$field->getItems()->column('Title'));
617
		$this->assertNotContains('File3',$field->getItems()->column('Title'));
618
		$this->assertContains('File4',$field->getItems()->column('Title'));
619
		$this->assertContains('File5',$field->getItems()->column('Title'));
620
	}
621
622 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...
623
		$response = $this->get('UploadFieldTest_Controller');
624
		$this->assertFalse($response->isError());
625
626
		$parser = new CSSContentParser($response->getBody());
627
628
		$this->assertFalse(
629
			(bool)$parser->getBySelector(
630
				'#UploadFieldTestForm_Form_ReadonlyField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'
631
				),
632
			'Removes all buttons on items');
633
		$this->assertFalse(
634
			(bool)$parser->getBySelector('#UploadFieldTestForm_Form_ReadonlyField .ss-uploadfield-dropzone'),
635
			'Removes dropzone'
636
		);
637
		$this->assertFalse(
638
			(bool)$parser->getBySelector(
639
				'#UploadFieldTestForm_Form_ReadonlyField .ss-uploadfield-addfile'
640
			),
641
			'Entire "add" area'
642
		);
643
	}
644
645 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...
646
		$response = $this->get('UploadFieldTest_Controller');
647
		$this->assertFalse($response->isError());
648
649
		$parser = new CSSContentParser($response->getBody());
650
		$this->assertFalse(
651
			(bool)$parser->getBySelector(
652
				'#UploadFieldTestForm_Form_DisabledField .ss-uploadfield-files .ss-uploadfield-item .ss-ui-button'
653
			),
654
			'Removes all buttons on items');
655
		$this->assertFalse((bool)$parser->getBySelector(
656
			'#UploadFieldTestForm_Form_DisabledField .ss-uploadfield-dropzone'
657
			),
658
			'Removes dropzone');
659
		$this->assertFalse(
660
			(bool)$parser->getBySelector('#UploadFieldTestForm_Form_DisabledField .ss-uploadfield-addfile'),
661
			'Entire "add" area'
662
		);
663
	}
664
665
	public function testCanUpload() {
666
		$response = $this->get('UploadFieldTest_Controller');
667
		$this->assertFalse($response->isError());
668
669
		$parser = new CSSContentParser($response->getBody());
670
		$this->assertFalse(
671
			(bool)$parser->getBySelector(
672
				'#UploadFieldTestForm_Form_CanUploadFalseField_Holder .ss-uploadfield-dropzone'
673
			),
674
			'Removes dropzone');
675
		$this->assertTrue(
676
			(bool)$parser->getBySelector(
677
				'#UploadFieldTestForm_Form_CanUploadFalseField_Holder .ss-uploadfield-fromfiles'
678
			),
679
			'Keeps "From files" button'
680
		);
681
	}
682
683
	public function testCanUploadWithPermissionCode() {
684
		$field = UploadField::create('MyField');
685
		Session::clear("loggedInAs");
686
687
		$field->setCanUpload(true);
688
		$this->assertTrue($field->canUpload());
689
690
		$field->setCanUpload(false);
691
		$this->assertFalse($field->canUpload());
692
693
		$this->loginWithPermission('ADMIN');
694
695
		$field->setCanUpload(false);
696
		$this->assertFalse($field->canUpload());
697
698
		$field->setCanUpload('ADMIN');
699
		$this->assertTrue($field->canUpload());
700
	}
701
702
	public function testCanAttachExisting() {
703
		$response = $this->get('UploadFieldTest_Controller');
704
		$this->assertFalse($response->isError());
705
706
		$parser = new CSSContentParser($response->getBody());
707
		$this->assertTrue(
708
			(bool)$parser->getBySelector(
709
				'#UploadFieldTestForm_Form_CanAttachExistingFalseField_Holder .ss-uploadfield-fromcomputer-fileinput'
710
			),
711
			'Keeps input file control'
712
		);
713
		$this->assertFalse(
714
			(bool)$parser->getBySelector(
715
				'#UploadFieldTestForm_Form_CanAttachExistingFalseField_Holder .ss-uploadfield-fromfiles'
716
			),
717
			'Removes "From files" button'
718
		);
719
720
		// Test requests to select files have the correct given permission
721
		$response2 = $this->get('UploadFieldTest_Controller/Form/field/CanAttachExistingFalseField/select');
722
		$this->assertEquals(403, $response2->getStatusCode());
723
		$response3 = $this->get('UploadFieldTest_Controller/Form/field/HasOneFile/select');
724
		$this->assertEquals(200, $response3->getStatusCode());
725
	}
726
727 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...
728
		$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...
729
		$file4 = $this->objFromFixture('File', 'file4');
730
		$fileSubfolder = $this->objFromFixture('File', 'file-subfolder');
731
732
		$response = $this->get('UploadFieldTest_Controller/Form/field/ManyManyFiles/select/');
733
		$this->assertFalse($response->isError());
734
735
		// A bit too much coupling with GridField, but a full template overload would make things too complex
736
		$parser = new CSSContentParser($response->getBody());
737
		$items = $parser->getBySelector('.ss-gridfield-item');
738
		$itemIDs = array_map(create_function('$el', 'return (int)$el["data-id"];'), $items);
739
		$this->assertContains($file4->ID, $itemIDs, 'Contains file in assigned folder');
740
		$this->assertContains($fileSubfolder->ID, $itemIDs, 'Contains file in subfolder');
741
	}
742
743 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...
744
		$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...
745
		$file4 = $this->objFromFixture('File', 'file4');
746
		$fileSubfolder = $this->objFromFixture('File', 'file-subfolder');
747
748
		$response = $this->get('UploadFieldTest_Controller/Form/field/HasManyDisplayFolder/select/');
749
		$this->assertFalse($response->isError());
750
751
		// A bit too much coupling with GridField, but a full template overload would make things too complex
752
		$parser = new CSSContentParser($response->getBody());
753
		$items = $parser->getBySelector('.ss-gridfield-item');
754
		$itemIDs = array_map(create_function('$el', 'return (int)$el["data-id"];'), $items);
755
		$this->assertContains($file4->ID, $itemIDs, 'Contains file in assigned folder');
756
		$this->assertNotContains($fileSubfolder->ID, $itemIDs, 'Does not contain file in subfolder');
757
	}
758
759
	/**
760
	 * Test that UploadField:overwriteWarning cannot overwrite Upload:replaceFile
761
	 */
762
	public function testConfigOverwriteWarningCannotRelaceFiles() {
763
		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...
764
		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...
765
			UploadField::config()->defaultConfig, array('overwriteWarning' => true)
766
		);
767
768
		$tmpFileName = 'testUploadBasic.txt';
769
		$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...
770
		$this->assertFalse($response->isError());
771
		$responseData = Convert::json2array($response->getBody());
772
		$uploadedFile = DataObject::get_by_id('File', (int) $responseData[0]['id']);
773
		$this->assertTrue(is_object($uploadedFile), 'The file object is created');
774
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile));
0 ignored issues
show
Documentation introduced by
$uploadedFile is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
775
776
		$tmpFileName = 'testUploadBasic.txt';
777
		$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...
778
		$this->assertFalse($response->isError());
779
		$responseData = Convert::json2array($response->getBody());
780
		$uploadedFile2 = DataObject::get_by_id('File', (int) $responseData[0]['id']);
781
		$this->assertTrue(is_object($uploadedFile2), 'The file object is created');
782
		$this->assertFileExists(AssetStoreTest_SpyStore::getLocalPath($uploadedFile2));
0 ignored issues
show
Documentation introduced by
$uploadedFile2 is of type object<SilverStripe\Model\DataObject>, but the function expects a object<SilverStripe\File...Storage\AssetContainer>.

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...
783
		$this->assertTrue(
784
			$uploadedFile->Filename !== $uploadedFile2->Filename,
785
			'Filename is not the same'
786
		);
787
		$this->assertTrue(
788
			$uploadedFile->ID !== $uploadedFile2->ID,
789
			'File database record is not the same'
790
		);
791
	}
792
793
	/**
794
	 * Tests that UploadField::fileexist works
795
	 */
796
	public function testFileExists() {
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 . "/UploadFieldTest/.protected/315ae4c3d4/$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/.protected/UploadedFiles/315ae4c3d4/$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/.protected/UploadedFiles/315ae4c3d4/$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
		$this->assertTrue($responseExists->isError());
850
		$this->assertContains('File is not a valid upload', $responseExists->getBody());
851
	}
852
853
	protected function getMockForm() {
854
		return new Form(new Controller(), 'Form', new FieldList(), new FieldList());
855
	}
856
857
	/**
858
	 * @return Array Emulating an entry in the $_FILES superglobal
859
	 */
860
	protected function getUploadFile($tmpFileName = 'UploadFieldTest-testUpload.txt') {
861
		$tmpFilePath = TEMP_FOLDER . '/' . $tmpFileName;
862
		$tmpFileContent = '';
863
		for($i=0; $i<10000; $i++) $tmpFileContent .= '0';
864
		file_put_contents($tmpFilePath, $tmpFileContent);
865
866
		// emulates the $_FILES array
867
		return array(
868
			'name' => array('Uploads' => array($tmpFileName)),
869
			'type' => array('Uploads' => array('text/plaintext')),
870
			'size' => array('Uploads' => array(filesize($tmpFilePath))),
871
			'tmp_name' => array('Uploads' => array($tmpFilePath)),
872
			'error' => array('Uploads' => array(UPLOAD_ERR_OK)),
873
		);
874
	}
875
876
	/**
877
	 * Simulates a form post to the test controller with the specified file IDs
878
	 *
879
	 * @param string $fileField Name of field to assign ids to
880
	 * @param array $ids list of file IDs
881
	 * @return boolean Array with key 'errors'
882
	 */
883
	protected function mockUploadFileIDs($fileField, $ids) {
884
885
		// collate file ids
886
		$files = array();
887
		foreach($ids as $id) {
888
			$files[$id] = $id;
889
		}
890
891
		$data = array(
892
			'action_submit' => 1
893
		);
894
		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...
895
			// Normal post requests can't submit empty array values for fields
896
			$data[$fileField] = array('Files' => $files);
897
		}
898
899
		$form = new UploadFieldTestForm();
900
		$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...
901
902 View Code Duplication
		if($form->validate()) {
0 ignored issues
show
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...
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 View Code Duplication
	protected function mockFileUpload($fileField, $tmpFileName) {
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...
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 get($url, $session = null, $headers = null, $cookies = null) {
977
		// Inject stage=Stage into the URL, to force working on draft
978
		$url = $this->addStageToUrl($url);
979
		return parent::get($url, $session, $headers, $cookies);
980
	}
981
982
	public function post($url, $data, $headers = null, $session = null, $body = null, $cookies = null) {
983
		// Inject stage=Stage into the URL, to force working on draft
984
		$url = $this->addStageToUrl($url);
985
		return parent::post($url, $data, $headers, $session, $body, $cookies);
986
	}
987
988
	/**
989
	 * Adds ?stage=Stage to url
990
	 *
991
	 * @param string $url
992
	 * @return string
993
	 */
994
	protected function addStageToUrl($url) {
995
		if(stripos($url, 'stage=Stage') === false) {
996
			if(stripos($url, '?') === false) {
997
				$url .= '?stage=Stage';
998
			} else {
999
				$url .= '&stage=Stage';
1000
			}
1001
		}
1002
		return $url;
1003
	}
1004
1005
}
1006
1007
class UploadFieldTest_Record extends DataObject implements TestOnly {
1008
1009
	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...
1010
		'Title' => 'Text',
1011
	);
1012
1013
	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...
1014
		'HasOneFile' => 'File',
1015
		'HasOneFileMaxOne' => 'File',
1016
		'HasOneFileMaxTwo' => 'File',
1017
		'HasOneExtendedFile' => 'UploadFieldTest_ExtendedFile'
1018
	);
1019
1020
	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...
1021
		'HasManyFiles' => 'File.HasManyRecord',
1022
		'HasManyFilesMaxTwo' => 'File.HasManyMaxTwoRecord',
1023
		'HasManyNoViewFiles' => 'File.HasManyNoViewRecord',
1024
		'ReadonlyField' => 'File.ReadonlyRecord'
1025
	);
1026
1027
	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...
1028
		'ManyManyFiles' => 'File'
1029
	);
1030
1031
}
1032
1033
class UploadFieldTest_FileExtension extends DataExtension implements TestOnly {
1034
1035
	private static $has_one = array(
1036
		'HasManyRecord' => 'UploadFieldTest_Record',
1037
		'HasManyMaxTwoRecord' => 'UploadFieldTest_Record',
1038
		'HasManyNoViewRecord' => 'UploadFieldTest_Record',
1039
		'ReadonlyRecord' => 'UploadFieldTest_Record'
1040
	);
1041
1042
	private static $has_many = array(
1043
		'HasOneRecords' => 'UploadFieldTest_Record.HasOneFile',
1044
		'HasOneMaxOneRecords' => 'UploadFieldTest_Record.HasOneFileMaxOne',
1045
		'HasOneMaxTwoRecords' => 'UploadFieldTest_Record.HasOneFileMaxTwo',
1046
	);
1047
1048
	private static $belongs_many_many = array(
1049
		'ManyManyRecords' => 'UploadFieldTest_Record'
1050
	);
1051
1052
	public function canDelete($member = null) {
1053
		if($this->owner->Name == 'nodelete.txt') return false;
1054
	}
1055
1056
	public function canEdit($member = null) {
1057
		if($this->owner->Name == 'noedit.txt') return false;
1058
	}
1059
1060
	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...
1061
		if($this->owner->Name == 'noview.txt') return false;
1062
	}
1063
}
1064
1065
/**
1066
 * Used for testing the create-on-upload
1067
 */
1068
class UploadFieldTest_ExtendedFile extends File implements TestOnly {
1069
1070
	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...
1071
		'HasOneExtendedRecords' => 'UploadFieldTest_Record.HasOneExtendedFile'
1072
	);
1073
}
1074
1075
class UploadFieldTestForm extends Form implements TestOnly {
1076
1077
	public function getRecord() {
1078
		if(empty($this->record)) {
1079
			$this->record = DataObject::get_one('UploadFieldTest_Record', '"Title" = \'Record 1\'');
1080
		}
1081
		return $this->record;
1082
	}
1083
1084
	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...
1085
		if(empty($controller)) {
1086
			$controller = new UploadFieldTest_Controller();
1087
		}
1088
1089
		$fieldRootFolder = UploadField::create('RootFolderTest')
1090
			->setFolderName('/');
1091
1092
		$fieldNoRelation = UploadField::create('NoRelationField')
1093
			->setFolderName('UploadedFiles');
1094
1095
		$fieldHasOne = UploadField::create('HasOneFile')
1096
			->setFolderName('UploadedFiles');
1097
1098
		$fieldHasOneExtendedFile = UploadField::create('HasOneExtendedFile')
1099
			->setFolderName('UploadedFiles');
1100
1101
		$fieldHasOneMaxOne = UploadField::create('HasOneFileMaxOne')
1102
			->setFolderName('UploadedFiles')
1103
			->setAllowedMaxFileNumber(1);
1104
1105
		$fieldHasOneMaxTwo = UploadField::create('HasOneFileMaxTwo')
1106
			->setFolderName('UploadedFiles')
1107
			->setAllowedMaxFileNumber(2);
1108
1109
		$fieldHasMany = UploadField::create('HasManyFiles')
1110
			->setFolderName('UploadedFiles');
1111
1112
		$fieldHasManyMaxTwo = UploadField::create('HasManyFilesMaxTwo')
1113
			->setFolderName('UploadedFiles')
1114
			->setAllowedMaxFileNumber(2);
1115
1116
		$fieldManyMany = UploadField::create('ManyManyFiles')
1117
			->setFolderName('UploadedFiles');
1118
1119
		$fieldHasManyNoView = UploadField::create('HasManyNoViewFiles')
1120
			->setFolderName('UploadedFiles');
1121
1122
		$fieldHasManyDisplayFolder = UploadField::create('HasManyDisplayFolder')
1123
			->setFolderName('UploadedFiles')
1124
			->setDisplayFolderName('UploadFieldTest');
1125
1126
		$fieldReadonly = UploadField::create('ReadonlyField')
1127
			->setFolderName('UploadedFiles')
1128
			->performReadonlyTransformation();
1129
1130
		$fieldDisabled = UploadField::create('DisabledField')
1131
			->setFolderName('UploadedFiles')
1132
			->performDisabledTransformation();
1133
1134
		$fieldSubfolder = UploadField::create('SubfolderField')
1135
			->setFolderName('UploadedFiles/subfolder1');
1136
1137
		$fieldCanUploadFalse = UploadField::create('CanUploadFalseField')
1138
			->setCanUpload(false);
1139
1140
		$fieldCanAttachExisting = UploadField::create('CanAttachExistingFalseField')
1141
			->setCanAttachExisting(false);
1142
1143
		$fieldAllowedExtensions = new UploadField('AllowedExtensionsField');
1144
		$fieldAllowedExtensions->getValidator()->setAllowedExtensions(array('txt'));
1145
1146
		$fieldInvalidAllowedExtensions = new UploadField('InvalidAllowedExtensionsField');
1147
		$fieldInvalidAllowedExtensions->getValidator()->setAllowedExtensions(array('txt', 'php'));
1148
1149
		$fields = new FieldList(
1150
			$fieldRootFolder,
1151
			$fieldNoRelation,
1152
			$fieldHasOne,
1153
			$fieldHasOneMaxOne,
1154
			$fieldHasOneMaxTwo,
1155
			$fieldHasOneExtendedFile,
1156
			$fieldHasMany,
1157
			$fieldHasManyMaxTwo,
1158
			$fieldManyMany,
1159
			$fieldHasManyNoView,
1160
			$fieldHasManyDisplayFolder,
1161
			$fieldReadonly,
1162
			$fieldDisabled,
1163
			$fieldSubfolder,
1164
			$fieldCanUploadFalse,
1165
			$fieldCanAttachExisting,
1166
			$fieldAllowedExtensions,
1167
			$fieldInvalidAllowedExtensions
1168
		);
1169
		$actions = new FieldList(
1170
			new FormAction('submit')
1171
		);
1172
		$validator = new RequiredFields();
1173
1174
		parent::__construct($controller, $name, $fields, $actions, $validator);
1175
1176
		$this->loadDataFrom($this->getRecord());
0 ignored issues
show
Documentation introduced by
$this->getRecord() is of type object<SilverStripe\Model\DataObject>, but the function expects a array|object<DataObject>.

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...
1177
	}
1178
1179
	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...
1180
		$record = $this->getRecord();
1181
		$form->saveInto($record);
1182
		$record->write();
1183
		return json_encode($record->toMap());
1184
	}
1185
}
1186
1187
1188
class UploadFieldTest_Controller extends Controller implements TestOnly {
1189
1190
	protected $template = 'BlankPage';
1191
1192
	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...
1193
1194
	public function Form() {
1195
		return new UploadFieldTestForm($this, 'Form');
1196
	}
1197
}
1198