Merge branch 'MDL-61480-master' of git://github.com/andrewnicols/moodle
[moodle.git] / lib / tests / filelib_test.php
blob01d03bdbd0ebce73efc9352f7ac6215afbdbfb31
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * Unit tests for /lib/filelib.php.
20 * @package core_files
21 * @category phpunit
22 * @copyright 2009 Jerome Mouneyrac
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/filelib.php');
30 require_once($CFG->dirroot . '/repository/lib.php');
32 class core_filelib_testcase extends advanced_testcase {
33 public function test_format_postdata_for_curlcall() {
35 // POST params with just simple types.
36 $postdatatoconvert = array( 'userid' => 1, 'roleid' => 22, 'name' => 'john');
37 $expectedresult = "userid=1&roleid=22&name=john";
38 $postdata = format_postdata_for_curlcall($postdatatoconvert);
39 $this->assertEquals($expectedresult, $postdata);
41 // POST params with a string containing & character.
42 $postdatatoconvert = array( 'name' => 'john&emilie', 'roleid' => 22);
43 $expectedresult = "name=john%26emilie&roleid=22"; // Urlencode: '%26' => '&'.
44 $postdata = format_postdata_for_curlcall($postdatatoconvert);
45 $this->assertEquals($expectedresult, $postdata);
47 // POST params with an empty value.
48 $postdatatoconvert = array( 'name' => null, 'roleid' => 22);
49 $expectedresult = "name=&roleid=22";
50 $postdata = format_postdata_for_curlcall($postdatatoconvert);
51 $this->assertEquals($expectedresult, $postdata);
53 // POST params with complex types.
54 $postdatatoconvert = array( 'users' => array(
55 array(
56 'id' => 2,
57 'customfields' => array(
58 array
60 'type' => 'Color',
61 'value' => 'violet'
67 $expectedresult = "users[0][id]=2&users[0][customfields][0][type]=Color&users[0][customfields][0][value]=violet";
68 $postdata = format_postdata_for_curlcall($postdatatoconvert);
69 $this->assertEquals($expectedresult, $postdata);
71 // POST params with other complex types.
72 $postdatatoconvert = array ('members' =>
73 array(
74 array('groupid' => 1, 'userid' => 1)
75 , array('groupid' => 1, 'userid' => 2)
78 $expectedresult = "members[0][groupid]=1&members[0][userid]=1&members[1][groupid]=1&members[1][userid]=2";
79 $postdata = format_postdata_for_curlcall($postdatatoconvert);
80 $this->assertEquals($expectedresult, $postdata);
83 public function test_download_file_content() {
84 global $CFG;
86 // Test http success first.
87 $testhtml = $this->getExternalTestFileUrl('/test.html');
89 $contents = download_file_content($testhtml);
90 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
92 $tofile = "$CFG->tempdir/test.html";
93 @unlink($tofile);
94 $result = download_file_content($testhtml, null, null, false, 300, 20, false, $tofile);
95 $this->assertTrue($result);
96 $this->assertFileExists($tofile);
97 $this->assertSame(file_get_contents($tofile), $contents);
98 @unlink($tofile);
100 $result = download_file_content($testhtml, null, null, false, 300, 20, false, null, true);
101 $this->assertSame($contents, $result);
103 $response = download_file_content($testhtml, null, null, true);
104 $this->assertInstanceOf('stdClass', $response);
105 $this->assertSame('200', $response->status);
106 $this->assertTrue(is_array($response->headers));
107 $this->assertRegExp('|^HTTP/1\.[01] 200 OK$|', rtrim($response->response_code));
108 $this->assertSame($contents, $response->results);
109 $this->assertSame('', $response->error);
111 // Test https success.
112 $testhtml = $this->getExternalTestFileUrl('/test.html', true);
114 $contents = download_file_content($testhtml, null, null, false, 300, 20, true);
115 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
117 $contents = download_file_content($testhtml);
118 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
120 // Now 404.
121 $testhtml = $this->getExternalTestFileUrl('/test.html_nonexistent');
123 $contents = download_file_content($testhtml);
124 $this->assertFalse($contents);
125 $this->assertDebuggingCalled();
127 $response = download_file_content($testhtml, null, null, true);
128 $this->assertInstanceOf('stdClass', $response);
129 $this->assertSame('404', $response->status);
130 $this->assertTrue(is_array($response->headers));
131 $this->assertRegExp('|^HTTP/1\.[01] 404 Not Found$|', rtrim($response->response_code));
132 // Do not test the response starts with DOCTYPE here because some servers may return different headers.
133 $this->assertSame('', $response->error);
135 // Invalid url.
136 $testhtml = $this->getExternalTestFileUrl('/test.html');
137 $testhtml = str_replace('http://', 'ftp://', $testhtml);
139 $contents = download_file_content($testhtml);
140 $this->assertFalse($contents);
142 // Test standard redirects.
143 $testurl = $this->getExternalTestFileUrl('/test_redir.php');
145 $contents = download_file_content("$testurl?redir=2");
146 $this->assertSame('done', $contents);
148 $response = download_file_content("$testurl?redir=2", null, null, true);
149 $this->assertInstanceOf('stdClass', $response);
150 $this->assertSame('200', $response->status);
151 $this->assertTrue(is_array($response->headers));
152 $this->assertRegExp('|^HTTP/1\.[01] 200 OK$|', rtrim($response->response_code));
153 $this->assertSame('done', $response->results);
154 $this->assertSame('', $response->error);
156 // Commented out this block if there are performance problems.
158 $contents = download_file_content("$testurl?redir=6");
159 $this->assertFalse(false, $contents);
160 $this->assertDebuggingCalled();
161 $response = download_file_content("$testurl?redir=6", null, null, true);
162 $this->assertInstanceOf('stdClass', $response);
163 $this->assertSame('0', $response->status);
164 $this->assertTrue(is_array($response->headers));
165 $this->assertFalse($response->results);
166 $this->assertNotEmpty($response->error);
169 // Test relative redirects.
170 $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
172 $contents = download_file_content("$testurl");
173 $this->assertSame('done', $contents);
175 $contents = download_file_content("$testurl?unused=xxx");
176 $this->assertSame('done', $contents);
180 * Test curl basics.
182 public function test_curl_basics() {
183 global $CFG;
185 // Test HTTP success.
186 $testhtml = $this->getExternalTestFileUrl('/test.html');
188 $curl = new curl();
189 $contents = $curl->get($testhtml);
190 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
191 $this->assertSame(0, $curl->get_errno());
193 $curl = new curl();
194 $tofile = "$CFG->tempdir/test.html";
195 @unlink($tofile);
196 $fp = fopen($tofile, 'w');
197 $result = $curl->get($testhtml, array(), array('CURLOPT_FILE'=>$fp));
198 $this->assertTrue($result);
199 fclose($fp);
200 $this->assertFileExists($tofile);
201 $this->assertSame($contents, file_get_contents($tofile));
202 @unlink($tofile);
204 $curl = new curl();
205 $tofile = "$CFG->tempdir/test.html";
206 @unlink($tofile);
207 $result = $curl->download_one($testhtml, array(), array('filepath'=>$tofile));
208 $this->assertTrue($result);
209 $this->assertFileExists($tofile);
210 $this->assertSame($contents, file_get_contents($tofile));
211 @unlink($tofile);
213 // Test 404 request.
214 $curl = new curl();
215 $contents = $curl->get($this->getExternalTestFileUrl('/i.do.not.exist'));
216 $response = $curl->getResponse();
217 $this->assertSame('404 Not Found', reset($response));
218 $this->assertSame(0, $curl->get_errno());
222 * Test a curl basic request with security enabled.
224 public function test_curl_basics_with_security_helper() {
225 $this->resetAfterTest();
227 // Test a request with a basic hostname filter applied.
228 $testhtml = $this->getExternalTestFileUrl('/test.html');
229 $url = new moodle_url($testhtml);
230 $host = $url->get_host();
231 set_config('curlsecurityblockedhosts', $host); // Blocks $host.
233 // Create curl with the default security enabled. We expect this to be blocked.
234 $curl = new curl();
235 $contents = $curl->get($testhtml);
236 $expected = $curl->get_security()->get_blocked_url_string();
237 $this->assertSame($expected, $contents);
238 $this->assertSame(0, $curl->get_errno());
240 // Now, create a curl using the 'ignoresecurity' override.
241 // We expect this request to pass, despite the admin setting having been set earlier.
242 $curl = new curl(['ignoresecurity' => true]);
243 $contents = $curl->get($testhtml);
244 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
245 $this->assertSame(0, $curl->get_errno());
247 // Now, try injecting a mock security helper into curl. This will override the default helper.
248 $mockhelper = $this->getMockBuilder('\core\files\curl_security_helper')->getMock();
250 // Make the mock return a different string.
251 $mockhelper->expects($this->any())->method('get_blocked_url_string')->will($this->returnValue('You shall not pass'));
253 // And make the mock security helper block all URLs. This helper instance doesn't care about config.
254 $mockhelper->expects($this->any())->method('url_is_blocked')->will($this->returnValue(true));
256 $curl = new curl(['securityhelper' => $mockhelper]);
257 $contents = $curl->get($testhtml);
258 $this->assertSame('You shall not pass', $curl->get_security()->get_blocked_url_string());
259 $this->assertSame($curl->get_security()->get_blocked_url_string(), $contents);
262 public function test_curl_redirects() {
263 global $CFG;
265 // Test full URL redirects.
266 $testurl = $this->getExternalTestFileUrl('/test_redir.php');
268 $curl = new curl();
269 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
270 $response = $curl->getResponse();
271 $this->assertSame('200 OK', reset($response));
272 $this->assertSame(0, $curl->get_errno());
273 $this->assertSame(2, $curl->info['redirect_count']);
274 $this->assertSame('done', $contents);
276 $curl = new curl();
277 $curl->emulateredirects = true;
278 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>2));
279 $response = $curl->getResponse();
280 $this->assertSame('200 OK', reset($response));
281 $this->assertSame(0, $curl->get_errno());
282 $this->assertSame(2, $curl->info['redirect_count']);
283 $this->assertSame('done', $contents);
285 // This test was failing for people behind Squid proxies. Squid does not
286 // fully support HTTP 1.1, so converts things to HTTP 1.0, where the name
287 // of the status code is different.
288 reset($response);
289 if (key($response) === 'HTTP/1.0') {
290 $responsecode302 = '302 Moved Temporarily';
291 } else {
292 $responsecode302 = '302 Found';
295 $curl = new curl();
296 $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
297 $response = $curl->getResponse();
298 $this->assertSame($responsecode302, reset($response));
299 $this->assertSame(0, $curl->get_errno());
300 $this->assertSame(302, $curl->info['http_code']);
301 $this->assertSame('', $contents);
303 $curl = new curl();
304 $curl->emulateredirects = true;
305 $contents = $curl->get("$testurl?redir=3", array(), array('CURLOPT_FOLLOWLOCATION'=>0));
306 $response = $curl->getResponse();
307 $this->assertSame($responsecode302, reset($response));
308 $this->assertSame(0, $curl->get_errno());
309 $this->assertSame(302, $curl->info['http_code']);
310 $this->assertSame('', $contents);
312 $curl = new curl();
313 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1));
314 $this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno());
315 $this->assertNotEmpty($contents);
317 $curl = new curl();
318 $curl->emulateredirects = true;
319 $contents = $curl->get("$testurl?redir=2", array(), array('CURLOPT_MAXREDIRS'=>1));
320 $this->assertSame(CURLE_TOO_MANY_REDIRECTS, $curl->get_errno());
321 $this->assertNotEmpty($contents);
323 $curl = new curl();
324 $tofile = "$CFG->tempdir/test.html";
325 @unlink($tofile);
326 $fp = fopen($tofile, 'w');
327 $result = $curl->get("$testurl?redir=1", array(), array('CURLOPT_FILE'=>$fp));
328 $this->assertTrue($result);
329 fclose($fp);
330 $this->assertFileExists($tofile);
331 $this->assertSame('done', file_get_contents($tofile));
332 @unlink($tofile);
334 $curl = new curl();
335 $curl->emulateredirects = true;
336 $tofile = "$CFG->tempdir/test.html";
337 @unlink($tofile);
338 $fp = fopen($tofile, 'w');
339 $result = $curl->get("$testurl?redir=1", array(), array('CURLOPT_FILE'=>$fp));
340 $this->assertTrue($result);
341 fclose($fp);
342 $this->assertFileExists($tofile);
343 $this->assertSame('done', file_get_contents($tofile));
344 @unlink($tofile);
346 $curl = new curl();
347 $tofile = "$CFG->tempdir/test.html";
348 @unlink($tofile);
349 $result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile));
350 $this->assertTrue($result);
351 $this->assertFileExists($tofile);
352 $this->assertSame('done', file_get_contents($tofile));
353 @unlink($tofile);
355 $curl = new curl();
356 $curl->emulateredirects = true;
357 $tofile = "$CFG->tempdir/test.html";
358 @unlink($tofile);
359 $result = $curl->download_one("$testurl?redir=1", array(), array('filepath'=>$tofile));
360 $this->assertTrue($result);
361 $this->assertFileExists($tofile);
362 $this->assertSame('done', file_get_contents($tofile));
363 @unlink($tofile);
366 public function test_curl_relative_redirects() {
367 // Test relative location redirects.
368 $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
370 $curl = new curl();
371 $contents = $curl->get($testurl);
372 $response = $curl->getResponse();
373 $this->assertSame('200 OK', reset($response));
374 $this->assertSame(0, $curl->get_errno());
375 $this->assertSame(1, $curl->info['redirect_count']);
376 $this->assertSame('done', $contents);
378 $curl = new curl();
379 $curl->emulateredirects = true;
380 $contents = $curl->get($testurl);
381 $response = $curl->getResponse();
382 $this->assertSame('200 OK', reset($response));
383 $this->assertSame(0, $curl->get_errno());
384 $this->assertSame(1, $curl->info['redirect_count']);
385 $this->assertSame('done', $contents);
387 // Test different redirect types.
388 $testurl = $this->getExternalTestFileUrl('/test_relative_redir.php');
390 $curl = new curl();
391 $contents = $curl->get("$testurl?type=301");
392 $response = $curl->getResponse();
393 $this->assertSame('200 OK', reset($response));
394 $this->assertSame(0, $curl->get_errno());
395 $this->assertSame(1, $curl->info['redirect_count']);
396 $this->assertSame('done', $contents);
398 $curl = new curl();
399 $curl->emulateredirects = true;
400 $contents = $curl->get("$testurl?type=301");
401 $response = $curl->getResponse();
402 $this->assertSame('200 OK', reset($response));
403 $this->assertSame(0, $curl->get_errno());
404 $this->assertSame(1, $curl->info['redirect_count']);
405 $this->assertSame('done', $contents);
407 $curl = new curl();
408 $contents = $curl->get("$testurl?type=302");
409 $response = $curl->getResponse();
410 $this->assertSame('200 OK', reset($response));
411 $this->assertSame(0, $curl->get_errno());
412 $this->assertSame(1, $curl->info['redirect_count']);
413 $this->assertSame('done', $contents);
415 $curl = new curl();
416 $curl->emulateredirects = true;
417 $contents = $curl->get("$testurl?type=302");
418 $response = $curl->getResponse();
419 $this->assertSame('200 OK', reset($response));
420 $this->assertSame(0, $curl->get_errno());
421 $this->assertSame(1, $curl->info['redirect_count']);
422 $this->assertSame('done', $contents);
424 $curl = new curl();
425 $contents = $curl->get("$testurl?type=303");
426 $response = $curl->getResponse();
427 $this->assertSame('200 OK', reset($response));
428 $this->assertSame(0, $curl->get_errno());
429 $this->assertSame(1, $curl->info['redirect_count']);
430 $this->assertSame('done', $contents);
432 $curl = new curl();
433 $curl->emulateredirects = true;
434 $contents = $curl->get("$testurl?type=303");
435 $response = $curl->getResponse();
436 $this->assertSame('200 OK', reset($response));
437 $this->assertSame(0, $curl->get_errno());
438 $this->assertSame(1, $curl->info['redirect_count']);
439 $this->assertSame('done', $contents);
441 $curl = new curl();
442 $contents = $curl->get("$testurl?type=307");
443 $response = $curl->getResponse();
444 $this->assertSame('200 OK', reset($response));
445 $this->assertSame(0, $curl->get_errno());
446 $this->assertSame(1, $curl->info['redirect_count']);
447 $this->assertSame('done', $contents);
449 $curl = new curl();
450 $curl->emulateredirects = true;
451 $contents = $curl->get("$testurl?type=307");
452 $response = $curl->getResponse();
453 $this->assertSame('200 OK', reset($response));
454 $this->assertSame(0, $curl->get_errno());
455 $this->assertSame(1, $curl->info['redirect_count']);
456 $this->assertSame('done', $contents);
458 $curl = new curl();
459 $contents = $curl->get("$testurl?type=308");
460 $response = $curl->getResponse();
461 $this->assertSame('200 OK', reset($response));
462 $this->assertSame(0, $curl->get_errno());
463 $this->assertSame(1, $curl->info['redirect_count']);
464 $this->assertSame('done', $contents);
466 $curl = new curl();
467 $curl->emulateredirects = true;
468 $contents = $curl->get("$testurl?type=308");
469 $response = $curl->getResponse();
470 $this->assertSame('200 OK', reset($response));
471 $this->assertSame(0, $curl->get_errno());
472 $this->assertSame(1, $curl->info['redirect_count']);
473 $this->assertSame('done', $contents);
477 public function test_curl_proxybypass() {
478 global $CFG;
479 $testurl = $this->getExternalTestFileUrl('/test.html');
481 $oldproxy = $CFG->proxyhost;
482 $oldproxybypass = $CFG->proxybypass;
484 // Test without proxy bypass and inaccessible proxy.
485 $CFG->proxyhost = 'i.do.not.exist';
486 $CFG->proxybypass = '';
487 $curl = new curl();
488 $contents = $curl->get($testurl);
489 $this->assertNotEquals(0, $curl->get_errno());
490 $this->assertNotEquals('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
492 // Test with proxy bypass.
493 $testurlhost = parse_url($testurl, PHP_URL_HOST);
494 $CFG->proxybypass = $testurlhost;
495 $curl = new curl();
496 $contents = $curl->get($testurl);
497 $this->assertSame(0, $curl->get_errno());
498 $this->assertSame('47250a973d1b88d9445f94db4ef2c97a', md5($contents));
500 $CFG->proxyhost = $oldproxy;
501 $CFG->proxybypass = $oldproxybypass;
505 * Test that duplicate lines in the curl header are removed.
507 public function test_duplicate_curl_header() {
508 $testurl = $this->getExternalTestFileUrl('/test_post.php');
510 $curl = new curl();
511 $headerdata = 'Accept: application/json';
512 $header = [$headerdata, $headerdata];
513 $this->assertCount(2, $header);
514 $curl->setHeader($header);
515 $this->assertCount(1, $curl->header);
516 $this->assertEquals($headerdata, $curl->header[0]);
519 public function test_curl_post() {
520 $testurl = $this->getExternalTestFileUrl('/test_post.php');
522 // Test post request.
523 $curl = new curl();
524 $contents = $curl->post($testurl, 'data=moodletest');
525 $response = $curl->getResponse();
526 $this->assertSame('200 OK', reset($response));
527 $this->assertSame(0, $curl->get_errno());
528 $this->assertSame('OK', $contents);
530 // Test 100 requests.
531 $curl = new curl();
532 $curl->setHeader('Expect: 100-continue');
533 $contents = $curl->post($testurl, 'data=moodletest');
534 $response = $curl->getResponse();
535 $this->assertSame('200 OK', reset($response));
536 $this->assertSame(0, $curl->get_errno());
537 $this->assertSame('OK', $contents);
540 public function test_curl_file() {
541 $this->resetAfterTest();
542 $testurl = $this->getExternalTestFileUrl('/test_file.php');
544 $fs = get_file_storage();
545 $filerecord = array(
546 'contextid' => context_system::instance()->id,
547 'component' => 'test',
548 'filearea' => 'curl_post',
549 'itemid' => 0,
550 'filepath' => '/',
551 'filename' => 'test.txt'
553 $teststring = 'moodletest';
554 $testfile = $fs->create_file_from_string($filerecord, $teststring);
556 // Test post with file.
557 $data = array('testfile' => $testfile);
558 $curl = new curl();
559 $contents = $curl->post($testurl, $data);
560 $this->assertSame('OK', $contents);
563 public function test_curl_file_name() {
564 $this->resetAfterTest();
565 $testurl = $this->getExternalTestFileUrl('/test_file_name.php');
567 $fs = get_file_storage();
568 $filerecord = array(
569 'contextid' => context_system::instance()->id,
570 'component' => 'test',
571 'filearea' => 'curl_post',
572 'itemid' => 0,
573 'filepath' => '/',
574 'filename' => 'test.txt'
576 $teststring = 'moodletest';
577 $testfile = $fs->create_file_from_string($filerecord, $teststring);
579 // Test post with file.
580 $data = array('testfile' => $testfile);
581 $curl = new curl();
582 $contents = $curl->post($testurl, $data);
583 $this->assertSame('OK', $contents);
586 public function test_curl_protocols() {
588 // HTTP and HTTPS requests were verified in previous requests. Now check
589 // that we can selectively disable some protocols.
590 $curl = new curl();
592 // Other protocols than HTTP(S) are disabled by default.
593 $testurl = 'file:///';
594 $curl->get($testurl);
595 $this->assertNotEmpty($curl->error);
596 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
598 $testurl = 'ftp://nowhere';
599 $curl->get($testurl);
600 $this->assertNotEmpty($curl->error);
601 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
603 $testurl = 'telnet://somewhere';
604 $curl->get($testurl);
605 $this->assertNotEmpty($curl->error);
606 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
608 // Protocols are also disabled during redirections.
609 $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
610 $curl->get($testurl, array('proto' => 'file'));
611 $this->assertNotEmpty($curl->error);
612 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
614 $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
615 $curl->get($testurl, array('proto' => 'ftp'));
616 $this->assertNotEmpty($curl->error);
617 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
619 $testurl = $this->getExternalTestFileUrl('/test_redir_proto.php');
620 $curl->get($testurl, array('proto' => 'telnet'));
621 $this->assertNotEmpty($curl->error);
622 $this->assertEquals(CURLE_UNSUPPORTED_PROTOCOL, $curl->errno);
626 * Testing prepare draft area
628 * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
629 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
631 public function test_prepare_draft_area() {
632 global $USER, $DB;
634 $this->resetAfterTest(true);
636 $generator = $this->getDataGenerator();
637 $user = $generator->create_user();
638 $usercontext = context_user::instance($user->id);
639 $USER = $DB->get_record('user', array('id'=>$user->id));
641 $repositorypluginname = 'user';
643 $args = array();
644 $args['type'] = $repositorypluginname;
645 $repos = repository::get_instances($args);
646 $userrepository = reset($repos);
647 $this->assertInstanceOf('repository', $userrepository);
649 $fs = get_file_storage();
651 $syscontext = context_system::instance();
652 $component = 'core';
653 $filearea = 'unittest';
654 $itemid = 0;
655 $filepath = '/';
656 $filename = 'test.txt';
657 $sourcefield = 'Copyright stuff';
659 $filerecord = array(
660 'contextid' => $syscontext->id,
661 'component' => $component,
662 'filearea' => $filearea,
663 'itemid' => $itemid,
664 'filepath' => $filepath,
665 'filename' => $filename,
666 'source' => $sourcefield,
668 $ref = $fs->pack_reference($filerecord);
669 $originalfile = $fs->create_file_from_string($filerecord, 'Test content');
670 $fileid = $originalfile->get_id();
671 $this->assertInstanceOf('stored_file', $originalfile);
673 // Create a user private file.
674 $userfilerecord = new stdClass;
675 $userfilerecord->contextid = $usercontext->id;
676 $userfilerecord->component = 'user';
677 $userfilerecord->filearea = 'private';
678 $userfilerecord->itemid = 0;
679 $userfilerecord->filepath = '/';
680 $userfilerecord->filename = 'userfile.txt';
681 $userfilerecord->source = 'test';
682 $userfile = $fs->create_file_from_string($userfilerecord, 'User file content');
683 $userfileref = $fs->pack_reference($userfilerecord);
685 $filerefrecord = clone((object)$filerecord);
686 $filerefrecord->filename = 'testref.txt';
688 // Create a file reference.
689 $fileref = $fs->create_file_from_reference($filerefrecord, $userrepository->id, $userfileref);
690 $this->assertInstanceOf('stored_file', $fileref);
691 $this->assertEquals($userrepository->id, $fileref->get_repository_id());
692 $this->assertSame($userfile->get_contenthash(), $fileref->get_contenthash());
693 $this->assertEquals($userfile->get_filesize(), $fileref->get_filesize());
694 $this->assertRegExp('#' . $userfile->get_filename(). '$#', $fileref->get_reference_details());
696 $draftitemid = 0;
697 file_prepare_draft_area($draftitemid, $syscontext->id, $component, $filearea, $itemid);
699 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid);
700 $this->assertCount(3, $draftfiles);
702 $draftfile = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $filepath, $filename);
703 $source = unserialize($draftfile->get_source());
704 $this->assertSame($ref, $source->original);
705 $this->assertSame($sourcefield, $source->source);
707 $draftfileref = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $filepath, $filerefrecord->filename);
708 $this->assertInstanceOf('stored_file', $draftfileref);
709 $this->assertTrue($draftfileref->is_external_file());
711 // Change some information.
712 $author = 'Dongsheng Cai';
713 $draftfile->set_author($author);
714 $newsourcefield = 'Get from Flickr';
715 $license = 'GPLv3';
716 $draftfile->set_license($license);
717 // If you want to really just change source field, do this.
718 $source = unserialize($draftfile->get_source());
719 $newsourcefield = 'From flickr';
720 $source->source = $newsourcefield;
721 $draftfile->set_source(serialize($source));
723 // Save changed file.
724 file_save_draft_area_files($draftitemid, $syscontext->id, $component, $filearea, $itemid);
726 $file = $fs->get_file($syscontext->id, $component, $filearea, $itemid, $filepath, $filename);
728 // Make sure it's the original file id.
729 $this->assertEquals($fileid, $file->get_id());
730 $this->assertInstanceOf('stored_file', $file);
731 $this->assertSame($author, $file->get_author());
732 $this->assertSame($license, $file->get_license());
733 $this->assertEquals($newsourcefield, $file->get_source());
737 * Testing deleting original files.
739 * @copyright 2012 Dongsheng Cai {@link http://dongsheng.org}
740 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
742 public function test_delete_original_file_from_draft() {
743 global $USER, $DB;
745 $this->resetAfterTest(true);
747 $generator = $this->getDataGenerator();
748 $user = $generator->create_user();
749 $usercontext = context_user::instance($user->id);
750 $USER = $DB->get_record('user', array('id'=>$user->id));
752 $repositorypluginname = 'user';
754 $args = array();
755 $args['type'] = $repositorypluginname;
756 $repos = repository::get_instances($args);
757 $userrepository = reset($repos);
758 $this->assertInstanceOf('repository', $userrepository);
760 $fs = get_file_storage();
761 $syscontext = context_system::instance();
763 $filecontent = 'User file content';
765 // Create a user private file.
766 $userfilerecord = new stdClass;
767 $userfilerecord->contextid = $usercontext->id;
768 $userfilerecord->component = 'user';
769 $userfilerecord->filearea = 'private';
770 $userfilerecord->itemid = 0;
771 $userfilerecord->filepath = '/';
772 $userfilerecord->filename = 'userfile.txt';
773 $userfilerecord->source = 'test';
774 $userfile = $fs->create_file_from_string($userfilerecord, $filecontent);
775 $userfileref = $fs->pack_reference($userfilerecord);
776 $contenthash = $userfile->get_contenthash();
778 $filerecord = array(
779 'contextid' => $syscontext->id,
780 'component' => 'core',
781 'filearea' => 'phpunit',
782 'itemid' => 0,
783 'filepath' => '/',
784 'filename' => 'test.txt',
786 // Create a file reference.
787 $fileref = $fs->create_file_from_reference($filerecord, $userrepository->id, $userfileref);
788 $this->assertInstanceOf('stored_file', $fileref);
789 $this->assertEquals($userrepository->id, $fileref->get_repository_id());
790 $this->assertSame($userfile->get_contenthash(), $fileref->get_contenthash());
791 $this->assertEquals($userfile->get_filesize(), $fileref->get_filesize());
792 $this->assertRegExp('#' . $userfile->get_filename(). '$#', $fileref->get_reference_details());
794 $draftitemid = 0;
795 file_prepare_draft_area($draftitemid, $usercontext->id, 'user', 'private', 0);
796 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid);
797 $this->assertCount(2, $draftfiles);
798 $draftfile = $fs->get_file($usercontext->id, 'user', 'draft', $draftitemid, $userfilerecord->filepath, $userfilerecord->filename);
799 $draftfile->delete();
800 // Save changed file.
801 file_save_draft_area_files($draftitemid, $usercontext->id, 'user', 'private', 0);
803 // The file reference should be a regular moodle file now.
804 $fileref = $fs->get_file($syscontext->id, 'core', 'phpunit', 0, '/', 'test.txt');
805 $this->assertFalse($fileref->is_external_file());
806 $this->assertSame($contenthash, $fileref->get_contenthash());
807 $this->assertEquals($filecontent, $fileref->get_content());
811 * Tests the strip_double_headers function in the curl class.
813 public function test_curl_strip_double_headers() {
814 // Example from issue tracker.
815 $mdl30648example = <<<EOF
816 HTTP/1.0 407 Proxy Authentication Required
817 Server: squid/2.7.STABLE9
818 Date: Thu, 08 Dec 2011 14:44:33 GMT
819 Content-Type: text/html
820 Content-Length: 1275
821 X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
822 Proxy-Authenticate: Basic realm="Squid proxy-caching web server"
823 X-Cache: MISS from homer.lancs.ac.uk
824 X-Cache-Lookup: NONE from homer.lancs.ac.uk:3128
825 Via: 1.0 homer.lancs.ac.uk:3128 (squid/2.7.STABLE9)
826 Connection: close
828 HTTP/1.0 200 OK
829 Server: Apache
830 X-Lb-Nocache: true
831 Cache-Control: private, max-age=15, no-transform
832 ETag: "4d69af5d8ba873ea9192c489e151bd7b"
833 Content-Type: text/html
834 Date: Thu, 08 Dec 2011 14:44:53 GMT
835 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk
836 X-Cache-Action: MISS
837 X-Cache-Age: 0
838 Vary: Cookie,X-Country,X-Ip-is-uk-combined,X-Ip-is-advertise-combined,X-Ip_is_uk_combined,X-Ip_is_advertise_combined, X-GeoIP
839 X-Cache: MISS from ww
841 <html>...
842 EOF;
843 $mdl30648expected = <<<EOF
844 HTTP/1.0 200 OK
845 Server: Apache
846 X-Lb-Nocache: true
847 Cache-Control: private, max-age=15, no-transform
848 ETag: "4d69af5d8ba873ea9192c489e151bd7b"
849 Content-Type: text/html
850 Date: Thu, 08 Dec 2011 14:44:53 GMT
851 Set-Cookie: BBC-UID=c4de2e109c8df6a51de627cee11b214bd4fb6054a030222488317afb31b343360MoodleBot/1.0; expires=Mon, 07-Dec-15 14:44:53 GMT; path=/; domain=bbc.co.uk
852 X-Cache-Action: MISS
853 X-Cache-Age: 0
854 Vary: Cookie,X-Country,X-Ip-is-uk-combined,X-Ip-is-advertise-combined,X-Ip_is_uk_combined,X-Ip_is_advertise_combined, X-GeoIP
855 X-Cache: MISS from ww
857 <html>...
858 EOF;
859 // For HTTP, replace the \n with \r\n.
860 $mdl30648example = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648example);
861 $mdl30648expected = preg_replace("~(?!<\r)\n~", "\r\n", $mdl30648expected);
863 // Test stripping works OK.
864 $this->assertSame($mdl30648expected, curl::strip_double_headers($mdl30648example));
865 // Test it does nothing to the 'plain' data.
866 $this->assertSame($mdl30648expected, curl::strip_double_headers($mdl30648expected));
868 // Example from OU proxy.
869 $httpsexample = <<<EOF
870 HTTP/1.0 200 Connection established
872 HTTP/1.1 200 OK
873 Date: Fri, 22 Feb 2013 17:14:23 GMT
874 Server: Apache/2
875 X-Powered-By: PHP/5.3.3-7+squeeze14
876 Content-Type: text/xml
877 Connection: close
878 Content-Encoding: gzip
879 Transfer-Encoding: chunked
881 <?xml version="1.0" encoding="ISO-8859-1" ?>
882 <rss version="2.0">...
883 EOF;
884 $httpsexpected = <<<EOF
885 HTTP/1.1 200 OK
886 Date: Fri, 22 Feb 2013 17:14:23 GMT
887 Server: Apache/2
888 X-Powered-By: PHP/5.3.3-7+squeeze14
889 Content-Type: text/xml
890 Connection: close
891 Content-Encoding: gzip
892 Transfer-Encoding: chunked
894 <?xml version="1.0" encoding="ISO-8859-1" ?>
895 <rss version="2.0">...
896 EOF;
897 // For HTTP, replace the \n with \r\n.
898 $httpsexample = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexample);
899 $httpsexpected = preg_replace("~(?!<\r)\n~", "\r\n", $httpsexpected);
901 // Test stripping works OK.
902 $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexample));
903 // Test it does nothing to the 'plain' data.
904 $this->assertSame($httpsexpected, curl::strip_double_headers($httpsexpected));
908 * Tests the get_mimetype_description function.
910 public function test_get_mimetype_description() {
911 $this->resetAfterTest();
913 // Test example type (.doc).
914 $this->assertEquals(get_string('application/msword', 'mimetypes'),
915 get_mimetype_description(array('filename' => 'test.doc')));
917 // Test an unknown file type.
918 $this->assertEquals(get_string('document/unknown', 'mimetypes'),
919 get_mimetype_description(array('filename' => 'test.frog')));
921 // Test a custom filetype with no lang string specified.
922 core_filetypes::add_type('frog', 'application/x-frog', 'document');
923 $this->assertEquals('application/x-frog',
924 get_mimetype_description(array('filename' => 'test.frog')));
926 // Test custom description.
927 core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document',
928 array(), '', 'Froggy file');
929 $this->assertEquals('Froggy file',
930 get_mimetype_description(array('filename' => 'test.frog')));
932 // Test custom description using multilang filter.
933 filter_manager::reset_caches();
934 filter_set_global_state('multilang', TEXTFILTER_ON);
935 filter_set_applies_to_strings('multilang', true);
936 core_filetypes::update_type('frog', 'frog', 'application/x-frog', 'document',
937 array(), '', '<span lang="en" class="multilang">Green amphibian</span>' .
938 '<span lang="fr" class="multilang">Amphibian vert</span>');
939 $this->assertEquals('Green amphibian',
940 get_mimetype_description(array('filename' => 'test.frog')));
944 * Tests the get_mimetypes_array function.
946 public function test_get_mimetypes_array() {
947 $mimeinfo = get_mimetypes_array();
949 // Test example MIME type (doc).
950 $this->assertEquals('application/msword', $mimeinfo['doc']['type']);
951 $this->assertEquals('document', $mimeinfo['doc']['icon']);
952 $this->assertEquals(array('document'), $mimeinfo['doc']['groups']);
953 $this->assertFalse(isset($mimeinfo['doc']['string']));
954 $this->assertFalse(isset($mimeinfo['doc']['defaulticon']));
955 $this->assertFalse(isset($mimeinfo['doc']['customdescription']));
957 // Check the less common fields using other examples.
958 $this->assertEquals('image', $mimeinfo['png']['string']);
959 $this->assertEquals(true, $mimeinfo['txt']['defaulticon']);
963 * Tests for get_mimetype_for_sending function.
965 public function test_get_mimetype_for_sending() {
966 // Without argument.
967 $this->assertEquals('application/octet-stream', get_mimetype_for_sending());
969 // Argument is null.
970 $this->assertEquals('application/octet-stream', get_mimetype_for_sending(null));
972 // Filename having no extension.
973 $this->assertEquals('application/octet-stream', get_mimetype_for_sending('filenamewithoutextension'));
975 // Test using the extensions listed from the get_mimetypes_array function.
976 $mimetypes = get_mimetypes_array();
977 foreach ($mimetypes as $ext => $info) {
978 if ($ext === 'xxx') {
979 $this->assertEquals('application/octet-stream', get_mimetype_for_sending('SampleFile.' . $ext));
980 } else {
981 $this->assertEquals($info['type'], get_mimetype_for_sending('SampleFile.' . $ext));
987 * Test curl agent settings.
989 public function test_curl_useragent() {
990 $curl = new testable_curl();
991 $options = $curl->get_options();
992 $this->assertNotEmpty($options);
994 $curl->call_apply_opt($options);
995 $this->assertTrue(in_array('User-Agent: MoodleBot/1.0', $curl->header));
996 $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header));
998 $options['CURLOPT_USERAGENT'] = 'Test/1.0';
999 $curl->call_apply_opt($options);
1000 $this->assertTrue(in_array('User-Agent: Test/1.0', $curl->header));
1001 $this->assertFalse(in_array('User-Agent: MoodleBot/1.0', $curl->header));
1003 $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.0');
1004 $curl->call_apply_opt();
1005 $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header));
1006 $this->assertFalse(in_array('User-Agent: Test/1.0', $curl->header));
1008 $curl->set_option('CURLOPT_USERAGENT', 'AnotherUserAgent/1.1');
1009 $options = $curl->get_options();
1010 $curl->call_apply_opt($options);
1011 $this->assertTrue(in_array('User-Agent: AnotherUserAgent/1.1', $curl->header));
1012 $this->assertFalse(in_array('User-Agent: AnotherUserAgent/1.0', $curl->header));
1014 $curl->unset_option('CURLOPT_USERAGENT');
1015 $curl->call_apply_opt();
1016 $this->assertTrue(in_array('User-Agent: MoodleBot/1.0', $curl->header));
1018 // Finally, test it via exttests, to ensure the agent is sent properly.
1019 // Matching.
1020 $testurl = $this->getExternalTestFileUrl('/test_agent.php');
1021 $extcurl = new curl();
1022 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'AnotherUserAgent/1.2'));
1023 $response = $extcurl->getResponse();
1024 $this->assertSame('200 OK', reset($response));
1025 $this->assertSame(0, $extcurl->get_errno());
1026 $this->assertSame('OK', $contents);
1027 // Not matching.
1028 $contents = $extcurl->get($testurl, array(), array('CURLOPT_USERAGENT' => 'NonMatchingUserAgent/1.2'));
1029 $response = $extcurl->getResponse();
1030 $this->assertSame('200 OK', reset($response));
1031 $this->assertSame(0, $extcurl->get_errno());
1032 $this->assertSame('', $contents);
1036 * Test file_rewrite_pluginfile_urls.
1038 public function test_file_rewrite_pluginfile_urls() {
1040 $syscontext = context_system::instance();
1041 $originaltext = 'Fake test with an image <img src="@@PLUGINFILE@@/image.png">';
1043 // Do the rewrite.
1044 $finaltext = file_rewrite_pluginfile_urls($originaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0);
1045 $this->assertContains("pluginfile.php", $finaltext);
1047 // Now undo.
1048 $options = array('reverse' => true);
1049 $finaltext = file_rewrite_pluginfile_urls($finaltext, 'pluginfile.php', $syscontext->id, 'user', 'private', 0, $options);
1051 // Compare the final text is the same that the original.
1052 $this->assertEquals($originaltext, $finaltext);
1056 * Helpter function to create draft files
1058 * @param array $filedata data for the file record (to not use defaults)
1059 * @return stored_file the stored file instance
1061 public static function create_draft_file($filedata = array()) {
1062 global $USER;
1064 $fs = get_file_storage();
1066 $filerecord = array(
1067 'component' => 'user',
1068 'filearea' => 'draft',
1069 'itemid' => isset($filedata['itemid']) ? $filedata['itemid'] : file_get_unused_draft_itemid(),
1070 'author' => isset($filedata['author']) ? $filedata['author'] : fullname($USER),
1071 'filepath' => isset($filedata['filepath']) ? $filedata['filepath'] : '/',
1072 'filename' => isset($filedata['filename']) ? $filedata['filename'] : 'file.txt',
1075 if (isset($filedata['contextid'])) {
1076 $filerecord['contextid'] = $filedata['contextid'];
1077 } else {
1078 $usercontext = context_user::instance($USER->id);
1079 $filerecord['contextid'] = $usercontext->id;
1081 $source = isset($filedata['source']) ? $filedata['source'] : serialize((object)array('source' => 'From string'));
1082 $content = isset($filedata['content']) ? $filedata['content'] : 'some content here';
1084 $file = $fs->create_file_from_string($filerecord, $content);
1085 $file->set_source($source);
1087 return $file;
1091 * Test file_merge_files_from_draft_area_into_filearea
1093 public function test_file_merge_files_from_draft_area_into_filearea() {
1094 global $USER, $CFG;
1096 $this->resetAfterTest(true);
1097 $this->setAdminUser();
1098 $fs = get_file_storage();
1099 $usercontext = context_user::instance($USER->id);
1101 // Create a draft file.
1102 $filename = 'data.txt';
1103 $filerecord = array(
1104 'filename' => $filename,
1106 $file = self::create_draft_file($filerecord);
1107 $draftitemid = $file->get_itemid();
1109 $maxbytes = $CFG->userquota;
1110 $maxareabytes = $CFG->userquota;
1111 $options = array('subdirs' => 1,
1112 'maxbytes' => $maxbytes,
1113 'maxfiles' => -1,
1114 'areamaxbytes' => $maxareabytes);
1116 // Add new file.
1117 file_merge_files_from_draft_area_into_filearea($draftitemid, $usercontext->id, 'user', 'private', 0, $options);
1119 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1120 // Directory and file.
1121 $this->assertCount(2, $files);
1122 $found = false;
1123 foreach ($files as $file) {
1124 if (!$file->is_directory()) {
1125 $found = true;
1126 $this->assertEquals($filename, $file->get_filename());
1127 $this->assertEquals('some content here', $file->get_content());
1130 $this->assertTrue($found);
1132 // Add two more files.
1133 $filerecord = array(
1134 'itemid' => $draftitemid,
1135 'filename' => 'second.txt',
1137 self::create_draft_file($filerecord);
1138 $filerecord = array(
1139 'itemid' => $draftitemid,
1140 'filename' => 'third.txt',
1142 $file = self::create_draft_file($filerecord);
1144 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options);
1146 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1147 $this->assertCount(4, $files);
1149 // Update contents of one file.
1150 $filerecord = array(
1151 'filename' => 'second.txt',
1152 'content' => 'new content',
1154 $file = self::create_draft_file($filerecord);
1155 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options);
1157 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1158 $this->assertCount(4, $files);
1159 $found = false;
1160 foreach ($files as $file) {
1161 if ($file->get_filename() == 'second.txt') {
1162 $found = true;
1163 $this->assertEquals('new content', $file->get_content());
1166 $this->assertTrue($found);
1168 // Update author.
1169 // Set different author in the current file.
1170 foreach ($files as $file) {
1171 if ($file->get_filename() == 'second.txt') {
1172 $file->set_author('Nobody');
1175 $filerecord = array(
1176 'filename' => 'second.txt',
1178 $file = self::create_draft_file($filerecord);
1180 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $usercontext->id, 'user', 'private', 0, $options);
1182 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1183 $this->assertCount(4, $files);
1184 $found = false;
1185 foreach ($files as $file) {
1186 if ($file->get_filename() == 'second.txt') {
1187 $found = true;
1188 $this->assertEquals(fullname($USER), $file->get_author());
1191 $this->assertTrue($found);
1196 * Test max area bytes for file_merge_files_from_draft_area_into_filearea
1198 public function test_file_merge_files_from_draft_area_into_filearea_max_area_bytes() {
1199 global $USER;
1201 $this->resetAfterTest(true);
1202 $this->setAdminUser();
1203 $fs = get_file_storage();
1205 $file = self::create_draft_file();
1206 $options = array('subdirs' => 1,
1207 'maxbytes' => 5,
1208 'maxfiles' => -1,
1209 'areamaxbytes' => 10);
1211 // Add new file.
1212 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options);
1213 $usercontext = context_user::instance($USER->id);
1214 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1215 $this->assertCount(0, $files);
1219 * Test max file bytes for file_merge_files_from_draft_area_into_filearea
1221 public function test_file_merge_files_from_draft_area_into_filearea_max_file_bytes() {
1222 global $USER;
1224 $this->resetAfterTest(true);
1225 // The admin has no restriction for max file uploads, so use a normal user.
1226 $user = $this->getDataGenerator()->create_user();
1227 $this->setUser($user);
1228 $fs = get_file_storage();
1230 $file = self::create_draft_file();
1231 $options = array('subdirs' => 1,
1232 'maxbytes' => 1,
1233 'maxfiles' => -1,
1234 'areamaxbytes' => 100);
1236 // Add new file.
1237 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options);
1238 $usercontext = context_user::instance($USER->id);
1239 // Check we only get the base directory, not a new file.
1240 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1241 $this->assertCount(1, $files);
1242 $file = array_shift($files);
1243 $this->assertTrue($file->is_directory());
1247 * Test max file number for file_merge_files_from_draft_area_into_filearea
1249 public function test_file_merge_files_from_draft_area_into_filearea_max_files() {
1250 global $USER;
1252 $this->resetAfterTest(true);
1253 $this->setAdminUser();
1254 $fs = get_file_storage();
1256 $file = self::create_draft_file();
1257 $options = array('subdirs' => 1,
1258 'maxbytes' => 1000,
1259 'maxfiles' => 0,
1260 'areamaxbytes' => 1000);
1262 // Add new file.
1263 file_merge_files_from_draft_area_into_filearea($file->get_itemid(), $file->get_contextid(), 'user', 'private', 0, $options);
1264 $usercontext = context_user::instance($USER->id);
1265 // Check we only get the base directory, not a new file.
1266 $files = $fs->get_area_files($usercontext->id, 'user', 'private', 0);
1267 $this->assertCount(1, $files);
1268 $file = array_shift($files);
1269 $this->assertTrue($file->is_directory());
1273 * Test file_get_draft_area_info.
1275 public function test_file_get_draft_area_info() {
1276 global $USER;
1278 $this->resetAfterTest(true);
1279 $this->setAdminUser();
1280 $fs = get_file_storage();
1282 $filerecord = array(
1283 'filename' => 'one.txt',
1285 $file = self::create_draft_file($filerecord);
1286 $size = $file->get_filesize();
1287 $draftitemid = $file->get_itemid();
1288 // Add another file.
1289 $filerecord = array(
1290 'itemid' => $draftitemid,
1291 'filename' => 'second.txt',
1293 $file = self::create_draft_file($filerecord);
1294 $size += $file->get_filesize();
1296 // Create directory.
1297 $usercontext = context_user::instance($USER->id);
1298 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/');
1299 // Add file to directory.
1300 $filerecord = array(
1301 'itemid' => $draftitemid,
1302 'filename' => 'third.txt',
1303 'filepath' => '/testsubdir/',
1305 $file = self::create_draft_file($filerecord);
1306 $size += $file->get_filesize();
1308 $fileinfo = file_get_draft_area_info($draftitemid);
1309 $this->assertEquals(3, $fileinfo['filecount']);
1310 $this->assertEquals($size, $fileinfo['filesize']);
1311 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created.
1312 $this->assertEquals($size, $fileinfo['filesize_without_references']);
1314 // Now get files from just one folder.
1315 $fileinfo = file_get_draft_area_info($draftitemid, '/testsubdir/');
1316 $this->assertEquals(1, $fileinfo['filecount']);
1317 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']);
1318 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory.
1319 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']);
1321 // Check we get the same results if we call file_get_file_area_info.
1322 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'draft', $draftitemid);
1323 $this->assertEquals(3, $fileinfo['filecount']);
1324 $this->assertEquals($size, $fileinfo['filesize']);
1325 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created.
1326 $this->assertEquals($size, $fileinfo['filesize_without_references']);
1330 * Test file_get_file_area_info.
1332 public function test_file_get_file_area_info() {
1333 global $USER;
1335 $this->resetAfterTest(true);
1336 $this->setAdminUser();
1337 $fs = get_file_storage();
1339 $filerecord = array(
1340 'filename' => 'one.txt',
1342 $file = self::create_draft_file($filerecord);
1343 $size = $file->get_filesize();
1344 $draftitemid = $file->get_itemid();
1345 // Add another file.
1346 $filerecord = array(
1347 'itemid' => $draftitemid,
1348 'filename' => 'second.txt',
1350 $file = self::create_draft_file($filerecord);
1351 $size += $file->get_filesize();
1353 // Create directory.
1354 $usercontext = context_user::instance($USER->id);
1355 $dir = $fs->create_directory($usercontext->id, 'user', 'draft', $draftitemid, '/testsubdir/');
1356 // Add file to directory.
1357 $filerecord = array(
1358 'itemid' => $draftitemid,
1359 'filename' => 'third.txt',
1360 'filepath' => '/testsubdir/',
1362 $file = self::create_draft_file($filerecord);
1363 $size += $file->get_filesize();
1365 // Add files to user private file area.
1366 $options = array('subdirs' => 1, 'maxfiles' => 3);
1367 file_merge_files_from_draft_area_into_filearea($draftitemid, $file->get_contextid(), 'user', 'private', 0, $options);
1369 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private');
1370 $this->assertEquals(3, $fileinfo['filecount']);
1371 $this->assertEquals($size, $fileinfo['filesize']);
1372 $this->assertEquals(1, $fileinfo['foldercount']); // Directory created.
1373 $this->assertEquals($size, $fileinfo['filesize_without_references']);
1375 // Now get files from just one folder.
1376 $fileinfo = file_get_file_area_info($usercontext->id, 'user', 'private', 0, '/testsubdir/');
1377 $this->assertEquals(1, $fileinfo['filecount']);
1378 $this->assertEquals($file->get_filesize(), $fileinfo['filesize']);
1379 $this->assertEquals(0, $fileinfo['foldercount']); // No subdirectories inside the directory.
1380 $this->assertEquals($file->get_filesize(), $fileinfo['filesize_without_references']);
1384 * Test confirming that draft files not referenced in the editor text are removed.
1386 public function test_file_remove_editor_orphaned_files() {
1387 global $USER, $CFG;
1388 $this->resetAfterTest(true);
1389 $this->setAdminUser();
1391 // Create three draft files.
1392 $filerecord = ['filename' => 'file1.png'];
1393 $file = self::create_draft_file($filerecord);
1394 $draftitemid = $file->get_itemid();
1396 $filerecord['itemid'] = $draftitemid;
1398 $filerecord['filename'] = 'file2.png';
1399 self::create_draft_file($filerecord);
1401 $filerecord['filename'] = 'file 3.png';
1402 self::create_draft_file($filerecord);
1404 // Confirm the user drafts area lists 3 files.
1405 $fs = get_file_storage();
1406 $usercontext = context_user::instance($USER->id);
1407 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0);
1408 $this->assertCount(3, $draftfiles);
1410 // Now, spoof some editor text content, referencing 2 of the files; one requiring name encoding, one not.
1411 $editor = [
1412 'itemid' => $draftitemid,
1413 'text' => '
1414 <img src="'.$CFG->wwwroot.'/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftitemid.'/file%203.png" alt="">
1415 <img src="'.$CFG->wwwroot.'/draftfile.php/'.$usercontext->id.'/user/draft/'.$draftitemid.'/file1.png" alt="">'
1418 // Run the remove orphaned drafts function and confirm that only the referenced files remain in the user drafts.
1419 $expected = ['file1.png', 'file 3.png']; // The drafts we expect will not be removed (are referenced in the online text).
1420 file_remove_editor_orphaned_files($editor);
1421 $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'itemid', 0);
1422 $this->assertCount(2, $draftfiles);
1423 foreach ($draftfiles as $file) {
1424 $this->assertContains($file->get_filename(), $expected);
1430 * Test-specific class to allow easier testing of curl functions.
1432 * @copyright 2015 Dave Cooper
1433 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1435 class testable_curl extends curl {
1437 * Accessor for private options array using reflection.
1439 * @return array
1441 public function get_options() {
1442 // Access to private property.
1443 $rp = new ReflectionProperty('curl', 'options');
1444 $rp->setAccessible(true);
1445 return $rp->getValue($this);
1449 * Setter for private options array using reflection.
1451 * @param array $options
1453 public function set_options($options) {
1454 // Access to private property.
1455 $rp = new ReflectionProperty('curl', 'options');
1456 $rp->setAccessible(true);
1457 $rp->setValue($this, $options);
1461 * Setter for individual option.
1462 * @param string $option
1463 * @param string $value
1465 public function set_option($option, $value) {
1466 $options = $this->get_options();
1467 $options[$option] = $value;
1468 $this->set_options($options);
1472 * Unsets an option on the curl object
1473 * @param string $option
1475 public function unset_option($option) {
1476 $options = $this->get_options();
1477 unset($options[$option]);
1478 $this->set_options($options);
1482 * Wrapper to access the private curl::apply_opt() method using reflection.
1484 * @param array $options
1485 * @return resource The curl handle
1487 public function call_apply_opt($options = null) {
1488 // Access to private method.
1489 $rm = new ReflectionMethod('curl', 'apply_opt');
1490 $rm->setAccessible(true);
1491 $ch = curl_init();
1492 return $rm->invoke($this, $ch, $options);