first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
test
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMinYMid meet">
<g id='gtop' stroke-width="12" stroke="#000">
<g id="svgstar" transform="translate(50,50)">
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
</g>
</g>
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
</svg>

After

Width:  |  Height:  |  Size: 778 B

+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" stroke="#000" preserveAspectRatio="xMinYMid meet">
<path d="M8,80s-5,8,5,9l78,0s9,0,5-9l-40-71s-4-6-8,0z" stroke-width="2" fill="#fff" fill-rule="evenodd" />
<path d="M52,10 L10,85 L93,85z" stroke-width="2" stroke-linejoin="round" fill="#fc0" fill-rule="evenodd"/>
<path d="M52,32l0,26" stroke-width="9" stroke-linecap="round" fill-rule="evenodd"/>
<circle r="6" cx="52" cy="73"/>
</svg>

After

Width:  |  Height:  |  Size: 458 B

+13
View File
@@ -0,0 +1,13 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMinYMid meet">
<g id='gtop' stroke-width="12" stroke="#000">
<g id="svgstar" transform="translate(50,50)">
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
</g>
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
</svg>

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" preserveAspectRatio="xMinYMid meet">
<g id='gtop' stroke-width="12" stroke="#000">
<g id="svgstar" transform="translate(50,50)">
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
</g>
</g>
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
</svg>

After

Width:  |  Height:  |  Size: 800 B

@@ -0,0 +1,14 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
]>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px" preserveAspectRatio="xMinYMid meet">
<g id='gtop' stroke-width="12" stroke="#000">
<g id="svgstar" transform="translate(50,50)">
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
</g>
</g>
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
</svg>

After

Width:  |  Height:  |  Size: 807 B

+55
View File
@@ -0,0 +1,55 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This debug script is used during zip support development.
*
* @package core_files
* @copyright 2012 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
$help =
"Create sample zip file for testing
Example:
\$php zip_create_test_file.php test.zip
";
if (count($_SERVER['argv']) != 2 or file_exists($_SERVER['argv'][1])) {
echo $help;
exit(0);
}
$archive = $_SERVER['argv'][1];
$packer = get_file_packer('application/zip');
$file = __DIR__.'/test.txt';
$files = array(
'test.test' => $file,
'testíček.txt' => $file,
'Prüfung.txt' => $file,
'测试.txt' => $file,
'試験.txt' => $file,
'Žluťoučký/Koníček.txt' => $file,
);
$packer->archive_to_pathname($files, $archive);
+265
View File
@@ -0,0 +1,265 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This debug script is used during zip support development ONLY.
*
* @package core_files
* @copyright 2012 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require(__DIR__.'/../../../../config.php');
require_once("$CFG->libdir/clilib.php");
require_once("$CFG->libdir/filestorage/zip_packer.php");
if (count($_SERVER['argv']) != 2 or !file_exists($_SERVER['argv'][1])) {
cli_error("This script expects zip file name as the only parameter");
}
$archive = $_SERVER['argv'][1];
// Note: the ZIP structure is described at http://www.pkware.com/documents/casestudies/APPNOTE.TXT
if (!$filesize = filesize($archive) or !$fp = fopen($archive, 'rb+')) {
cli_error("Can not open ZIP archive: $archive");
}
if ($filesize == 22) {
$info = unpack('Vsig', fread($fp, 4));
fclose($fp);
if ($info['sig'] == 0x6054b50) {
cli_error("This ZIP archive is empty: $archive");
} else {
cli_error("This is not a ZIP archive: $archive");
}
}
fseek($fp, 0);
$info = unpack('Vsig', fread($fp, 4));
if ($info['sig'] !== 0x04034b50) {
fclose($fp);
cli_error("This is not a ZIP archive: $archive");
}
// Find end of central directory record.
$centralend = zip_archive::zip_get_central_end($fp, $filesize);
if ($centralend === false) {
cli_error("This is not a ZIP archive: $archive");
}
if ($centralend['disk'] !== 0 or $centralend['disk_start'] !== 0) {
cli_error("Multi-disk archives are not supported: $archive");
}
if ($centralend['offset'] === 0xFFFFFFFF) {
cli_error("ZIP64 archives are not supported: $archive");
}
fseek($fp, $centralend['offset']);
$data = fread($fp, $centralend['size']);
$pos = 0;
$files = array();
for($i=0; $i<$centralend['entries']; $i++) {
$file = zip_archive::zip_parse_file_header($data, $centralend, $pos);
if ($file === false) {
cli_error('Invalid Zip file header structure: '.$archive);
}
// Read local file header.
fseek($fp, $file['local_offset']);
$localfile = unpack('Vsig/vversion_req/vgeneral/vmethod/Vmodified/Vcrc/Vsize_compressed/Vsize/vname_length/vextra_length', fread($fp, 30));
if ($localfile['sig'] !== 0x04034b50) {
// Borked file!
$file['error'] = 'Invalid local file signature';
$files[] = $file;
continue;
}
if ($localfile['name_length']) {
$localfile['name'] = fread($fp, $localfile['name_length']);
} else {
$localfile['name'] = '';
}
$localfile['extra'] = array();
$localfile['extra_data'] = '';
if ($localfile['extra_length']) {
$extradata = fread($fp, $localfile['extra_length']);
$localfile['extra_data'] = $extradata;
while (strlen($extradata) > 4) {
$extra = unpack('vid/vsize', substr($extradata, 0, 4));
$extra['data'] = substr($extradata, 4, $extra['size']);
$extradata = substr($extradata, 4+$extra['size']);
$localfile['extra'][] = $extra;
}
}
$file['local'] = $localfile;
$files[] = $file;
}
echo "Archive: $archive\n";
echo "Number of files: {$centralend['entries']}\n";
echo "Archive comment: \"{$centralend['comment']}\" ({$centralend['comment_length']} bytes)\n";
foreach ($files as $i=>$file) {
echo "======== File ".($i+1)." ==============================================\n";
echo " Name: ".zip_print_name($file['name'])."\n";
if ($file['comment'] !== '') {
echo " Comment: \"{$file['comment']}\" ({$file['comment_length']} bytes)\n";
}
echo " Version: 0x".str_pad(dechex($file['version']), 4, '0', STR_PAD_LEFT)."\n";
echo " Required: 0x".str_pad(dechex($file['version_req']), 4, '0', STR_PAD_LEFT)."\n";
echo " Method: ".zip_print_method($file['method'])."\n";
echo " General: ".zip_print_general($file['general'])."\n";
echo " Modified: ".userdate(zip_dos2unixtime($file['modified']))."\n";
echo " Size: ".zip_print_sizes($file['size'], $file['size_compressed'])."\n";
echo " CRC-32: {$file['crc']}\n";
foreach($file['extra'] as $j=>$extra) {
echo " Extra ".($j+1).": ".zip_print_extra($extra)."\n";
}
if (!empty($file['local']['error'])) {
echo " Local ERROR: {$file['local']['error']}\n";
continue;
}
$localfile = $file['local'];
if ($localfile['name'] !== $file['name']) {
echo " Local name: ".zip_print_name($localfile['name'])."\n";
}
if ($localfile['version_req'] !== $file['version_req']) {
echo " Local required: 0x".str_pad(dechex($localfile['version_req']), 4, '0', STR_PAD_LEFT)."\n";
}
if ($localfile['method'] !== $file['method']) {
echo " Local method: ".zip_print_method($localfile['method'])."\n";
}
if ($localfile['general'] !== $file['general']) {
echo " Local general: ".zip_print_general($localfile['general'])."\n";
}
if ($localfile['modified'] !== $file['modified']) {
echo " Local modified: ".userdate(zip_dos2unixtime($localfile['modified']))."\n";
}
if ($localfile['size'] !== $file['size']) {
echo " Local size: ".zip_print_sizes($localfile['size'], $localfile['size_compressed'])."\n";
}
if ($localfile['crc'] !== $file['crc']) {
echo " Local CRC-32: {$localfile['crc']}\n";
}
foreach($localfile['extra'] as $j=>$extra) {
echo " Local extra ".($j+1).": ".zip_print_extra($extra)."\n";
}
}
fclose($fp);
exit(0);
// === Some useful functions ======================================
function zip_print_name($name) {
$size = strlen($name);
$crc = crc32($name);
return "\"$name\" ($size bytes) - CRC $crc";
}
function zip_print_method($method) {
$desc = '';
switch($method) {
case 0: $desc = 'Stored'; break;
case 1: $desc = 'Shrunk'; break;
case 2: $desc = 'Reduced factor 1'; break;
case 3: $desc = 'Reduced factor 2'; break;
case 4: $desc = 'Reduced factor 3'; break;
case 5: $desc = 'Reduced factor 4'; break;
case 6: $desc = 'Imploded'; break;
case 8: $desc = 'Deflated'; break;
case 9: $desc = 'Deflate64'; break;
case 10: $desc = 'old IBM TERSE'; break;
case 12: $desc = 'BZIP2'; break;
case 14: $desc = 'LZMA'; break;
case 18: $desc = 'IBM TERSE'; break;
case 19: $desc = 'IBM LZ77'; break;
case 97: $desc = 'WavPack'; break;
case 98: $desc = 'PPMd v1'; break;
}
if ($desc) {
$desc = " ($desc)";
}
return "0x".str_pad(dechex($method), 4, '0', STR_PAD_LEFT).$desc;
}
function zip_print_general($general) {
$desc = array();
if ($general & pow(2, 0)) {
$desc[] = 'Encrypted';
}
if ($general & pow(2, 11)) {
$desc[] = 'Unicode name';
}
if ($desc) {
$desc = " (".implode(', ', $desc).")";
} else {
$desc = '';
}
return str_pad(decbin($general), 16, '0', STR_PAD_LEFT).$desc;
}
/**
* Convert MS date+time format to unix timestamp:
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms724274(v=vs.85).aspx
*
* Copied from: http://plugins.svn.wordpress.org/wp2epub/trunk/zipcreate/functions.lib.php
* author: redmonkey
* license: GPL
*/
function zip_dos2unixtime($dostime) {
$sec = 2 * ($dostime & 0x1f);
$min = ($dostime >> 5) & 0x3f;
$hrs = ($dostime >> 11) & 0x1f;
$day = ($dostime >> 16) & 0x1f;
$mon = ($dostime >> 21) & 0x0f;
$year = (($dostime >> 25) & 0x7f) + 1980;
return mktime($hrs, $min, $sec, $mon, $day, $year);
}
function zip_print_sizes($size, $compressed) {
return "$size ==> $compressed bytes";
}
function zip_print_extra($extra) {
$desc = '';
$info = "- ".bin2hex($extra['data'])." ({$extra['size']} bytes)";
switch($extra['id']) {
case 0x0009: $desc = 'OS/2'; break;
case 0x000a: $desc = 'NTFS'; break;
case 0x000d: $desc = 'UNIX'; break;
case 0x5455: $desc = 'Extended timestamp'; break;
case 0x5855: $desc = 'Infor-ZIP (original)'; break;
case 0x7075:
$desc = 'Info-ZIP Unicode path';
$data = unpack('cversion/Vcrc', substr($extra['data'], 0, 5));
$name = substr($extra['data'], 5);
$size = strlen($name);
if ($data['version'] === 1) {
$info = "- \"$name\" ($size bytes) - CRC {$data['crc']}";
}
break;
case 0x7865: $desc = 'Info-ZIP UNIX (new)'; break;
case 0x7875: $desc = 'Info-ZIP UNIX (3rd generation)'; break;
}
if ($desc) {
$desc = " ($desc)";
}
return "0x".str_pad(dechex($extra['id']), 4, '0', STR_PAD_LEFT)."$desc $info";
}
+142
View File
@@ -0,0 +1,142 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filestorage/file_progress.php');
/**
* Unit tests for /lib/filestorage/mbz_packer.php.
*
* @package core
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mbz_packer_test extends \advanced_testcase {
public function test_archive_with_both_options(): void {
global $CFG;
$this->resetAfterTest();
// Get backup packer.
$packer = get_file_packer('application/vnd.moodle.backup');
require_once($CFG->dirroot . '/lib/filestorage/tgz_packer.php');
// Set up basic archive contents.
$files = array('1.txt' => array('frog'));
// Create 2 archives (each with one file in) in zip mode.
$CFG->usezipbackups = true;
$filefalse = $CFG->tempdir . '/false.mbz';
$this->assertNotEmpty($packer->archive_to_pathname($files, $filefalse));
$context = \context_system::instance();
$this->assertNotEmpty($storagefalse = $packer->archive_to_storage(
$files, $context->id, 'phpunit', 'data', 0, '/', 'false.mbz'));
// Create 2 archives in tgz mode.
$CFG->usezipbackups = false;
$filetrue = $CFG->tempdir . '/true.mbz';
$this->assertNotEmpty($packer->archive_to_pathname($files, $filetrue));
$context = \context_system::instance();
$this->assertNotEmpty($storagetrue = $packer->archive_to_storage(
$files, $context->id, 'phpunit', 'data', 0, '/', 'true.mbz'));
// Check the sizes are different (indicating different formats).
$this->assertNotEquals(filesize($filefalse), filesize($filetrue));
$this->assertNotEquals($storagefalse->get_filesize(), $storagetrue->get_filesize());
// Extract files into storage and into filesystem from both formats.
// Extract to path (zip).
$packer->extract_to_pathname($filefalse, $CFG->tempdir);
$onefile = $CFG->tempdir . '/1.txt';
$this->assertEquals('frog', file_get_contents($onefile));
unlink($onefile);
// Extract to path (tgz).
$packer->extract_to_pathname($filetrue, $CFG->tempdir);
$onefile = $CFG->tempdir . '/1.txt';
$this->assertEquals('frog', file_get_contents($onefile));
unlink($onefile);
// Extract to storage (zip).
$packer->extract_to_storage($storagefalse, $context->id, 'phpunit', 'data', 1, '/');
$fs = get_file_storage();
$out = $fs->get_file($context->id, 'phpunit', 'data', 1, '/', '1.txt');
$this->assertNotEmpty($out);
$this->assertEquals('frog', $out->get_content());
// Extract to storage (tgz).
$packer->extract_to_storage($storagetrue, $context->id, 'phpunit', 'data', 2, '/');
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/', '1.txt');
$this->assertNotEmpty($out);
$this->assertEquals('frog', $out->get_content());
}
public function usezipbackups_provider() {
return [
'Use zips' => [true],
'Use tgz' => [false],
];
}
/**
* @dataProvider usezipbackups_provider
*/
public function test_extract_to_pathname_returnvalue_successful($usezipbackups): void {
global $CFG;
$this->resetAfterTest();
$packer = get_file_packer('application/vnd.moodle.backup');
// Set up basic archive contents.
$files = array('1.txt' => array('frog'));
// Create 2 archives (each with one file in) in zip mode.
$CFG->usezipbackups = $usezipbackups;
$mbzfile = make_request_directory() . '/file.mbz';
$packer->archive_to_pathname($files, $mbzfile);
$target = make_request_directory();
$result = $packer->extract_to_pathname($mbzfile, $target, null, null, true);
$this->assertTrue($result);
}
/**
* @dataProvider usezipbackups_provider
*/
public function test_extract_to_pathname_returnvalue_failure($usezipbackups): void {
global $CFG;
$this->resetAfterTest();
$packer = get_file_packer('application/vnd.moodle.backup');
// Create 2 archives (each with one file in) in zip mode.
$CFG->usezipbackups = $usezipbackups;
$mbzfile = make_request_directory() . '/file.mbz';
file_put_contents($mbzfile, 'Content');
$target = make_request_directory();
$result = $packer->extract_to_pathname($mbzfile, $target, null, null, true);
$this->assertDebuggingCalledCount(1);
$this->assertFalse($result);
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
// This file is part of Moodle - https://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use advanced_testcase;
use context_system;
/**
* Unit tests for lib/filestorage/stored_file.php.
*
* @package core_files
* @category test
* @covers \stored_file
* @copyright 2022 Mikhail Golenkov <mikhailgolenkov@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class stored_file_test extends advanced_testcase {
/**
* Test that the rotate_image() method does not rotate
* an image that is not supposed to be rotated.
* @covers ::rotate_image()
*/
public function test_rotate_image_does_not_rotate_image(): void {
global $CFG;
$this->resetAfterTest();
$filename = 'testimage.jpg';
$filepath = $CFG->dirroot . '/lib/filestorage/tests/fixtures/' . $filename;
$filerecord = [
'contextid' => context_system::instance()->id,
'component' => 'core',
'filearea' => 'unittest',
'itemid' => 0,
'filepath' => '/',
'filename' => $filename,
];
$fs = get_file_storage();
$storedfile = $fs->create_file_from_pathname($filerecord, $filepath);
$result = $storedfile->rotate_image();
$this->assertIsArray($result);
$this->assertCount(2, $result);
$this->assertFalse($result[0]);
$this->assertFalse($result[1]);
}
/**
* Test that the rotate_image() method rotates an image
* that is supposed to be rotated.
* @covers ::rotate_image()
*/
public function test_rotate_image_rotates_image(): void {
global $CFG;
$this->resetAfterTest();
// This image was manually rotated to be upside down. Also, Orientation, ExifImageWidth
// and ExifImageLength EXIF tags were written into its metadata.
// This is needed to make sure that this image will be rotated by stored_file::rotate_image()
// and stored as a new rotated file.
$filename = 'testimage_rotated.jpg';
$filepath = $CFG->dirroot . '/lib/filestorage/tests/fixtures/' . $filename;
$filerecord = [
'contextid' => context_system::instance()->id,
'component' => 'core',
'filearea' => 'unittest',
'itemid' => 0,
'filepath' => '/',
'filename' => $filename,
];
$fs = get_file_storage();
$storedfile = $fs->create_file_from_pathname($filerecord, $filepath);
list ($rotateddata, $size) = $storedfile->rotate_image();
$this->assertNotFalse($rotateddata);
$this->assertIsArray($size);
$this->assertEquals(1200, $size['width']);
$this->assertEquals(297, $size['height']);
}
/**
* Ensure that get_content_file_handle returns a valid file handle.
*
* @covers ::get_psr_stream
*/
public function test_get_psr_stream(): void {
global $CFG;
$this->resetAfterTest();
$filename = 'testimage.jpg';
$filepath = $CFG->dirroot . '/lib/filestorage/tests/fixtures/' . $filename;
$filerecord = [
'contextid' => context_system::instance()->id,
'component' => 'core',
'filearea' => 'unittest',
'itemid' => 0,
'filepath' => '/',
'filename' => $filename,
];
$fs = get_file_storage();
$file = $fs->create_file_from_pathname($filerecord, $filepath);
$stream = $file->get_psr_stream();
$this->assertInstanceOf(\Psr\Http\Message\StreamInterface::class, $stream);
$this->assertEquals(file_get_contents($filepath), $stream->getContents());
$this->assertFalse($stream->isWritable());
$stream->close();
}
}
+471
View File
@@ -0,0 +1,471 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use file_progress;
use tgz_packer;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filestorage/file_progress.php');
/**
* Unit tests for /lib/filestorage/tgz_packer.php and tgz_extractor.php.
*
* @package core
* @copyright 2013 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class tgz_packer_test extends \advanced_testcase implements file_progress {
/**
* @var array Progress information passed to the progress reporter
*/
protected $progress;
/**
* Puts contents with specified time.
*
* @param string $path File path
* @param string $contents Contents of file
* @param int $mtime Time modified
*/
protected static function file_put_contents_at_time($path, $contents, $mtime) {
file_put_contents($path, $contents);
touch($path, $mtime);
}
/**
* Set up some files to be archived.
*
* @return array Array listing files of all types
*/
protected function prepare_file_list() {
global $CFG;
$this->resetAfterTest(true);
// Make array listing files to archive.
$filelist = array();
// Normal file.
self::file_put_contents_at_time($CFG->tempdir . '/file1.txt', 'File 1', 1377993601);
$filelist['out1.txt'] = $CFG->tempdir . '/file1.txt';
// Recursive directory w/ file and directory with file.
check_dir_exists($CFG->tempdir . '/dir1/dir2');
self::file_put_contents_at_time($CFG->tempdir . '/dir1/file2.txt', 'File 2', 1377993602);
self::file_put_contents_at_time($CFG->tempdir . '/dir1/dir2/file3.txt', 'File 3', 1377993603);
$filelist['out2'] = $CFG->tempdir . '/dir1';
// Moodle stored_file.
$context = \context_system::instance();
$filerecord = array('contextid' => $context->id, 'component' => 'phpunit',
'filearea' => 'data', 'itemid' => 0, 'filepath' => '/',
'filename' => 'file4.txt', 'timemodified' => 1377993604);
$fs = get_file_storage();
$sf = $fs->create_file_from_string($filerecord, 'File 4');
$filelist['out3.txt'] = $sf;
// Moodle stored_file directory.
$filerecord['itemid'] = 1;
$filerecord['filepath'] = '/dir1/';
$filerecord['filename'] = 'file5.txt';
$filerecord['timemodified'] = 1377993605;
$fs->create_file_from_string($filerecord, 'File 5');
$filerecord['filepath'] = '/dir1/dir2/';
$filerecord['filename'] = 'file6.txt';
$filerecord['timemodified'] = 1377993606;
$fs->create_file_from_string($filerecord, 'File 6');
$filerecord['filepath'] = '/';
$filerecord['filename'] = 'excluded.txt';
$fs->create_file_from_string($filerecord, 'Excluded');
$filelist['out4'] = $fs->get_file($context->id, 'phpunit', 'data', 1, '/dir1/', '.');
// File stored as raw content.
$filelist['out5.txt'] = array('File 7');
// File where there's just an empty directory.
$filelist['out6'] = null;
return $filelist;
}
/**
* Tests getting the item.
*/
public function test_get_packer(): void {
$packer = get_file_packer('application/x-gzip');
$this->assertInstanceOf('tgz_packer', $packer);
}
/**
* Tests basic archive and extract to file paths.
*/
public function test_to_normal_files(): void {
global $CFG;
$packer = get_file_packer('application/x-gzip');
// Archive files.
$files = $this->prepare_file_list();
$archivefile = $CFG->tempdir . '/test.tar.gz';
$packer->archive_to_pathname($files, $archivefile);
// Extract same files.
$outdir = $CFG->tempdir . '/out';
check_dir_exists($outdir);
$result = $packer->extract_to_pathname($archivefile, $outdir);
// The result array should have file entries + directory entries for
// all implicit directories + entry for the explicit directory.
$expectedpaths = array('out1.txt', 'out2/', 'out2/dir2/', 'out2/dir2/file3.txt',
'out2/file2.txt', 'out3.txt', 'out4/', 'out4/dir2/', 'out4/file5.txt',
'out4/dir2/file6.txt', 'out5.txt', 'out6/');
sort($expectedpaths);
$actualpaths = array_keys($result);
sort($actualpaths);
$this->assertEquals($expectedpaths, $actualpaths);
foreach ($result as $path => $booleantrue) {
$this->assertTrue($booleantrue);
}
// Check the files are as expected.
$this->assertEquals('File 1', file_get_contents($outdir . '/out1.txt'));
$this->assertEquals('File 2', file_get_contents($outdir . '/out2/file2.txt'));
$this->assertEquals('File 3', file_get_contents($outdir . '/out2/dir2/file3.txt'));
$this->assertEquals('File 4', file_get_contents($outdir . '/out3.txt'));
$this->assertEquals('File 5', file_get_contents($outdir . '/out4/file5.txt'));
$this->assertEquals('File 6', file_get_contents($outdir . '/out4/dir2/file6.txt'));
$this->assertEquals('File 7', file_get_contents($outdir . '/out5.txt'));
$this->assertTrue(is_dir($outdir . '/out6'));
}
/**
* Tests archive and extract to Moodle file system.
*/
public function test_to_stored_files(): void {
global $CFG;
$packer = get_file_packer('application/x-gzip');
// Archive files.
$files = $this->prepare_file_list();
$archivefile = $CFG->tempdir . '/test.tar.gz';
$context = \context_system::instance();
$sf = $packer->archive_to_storage($files,
$context->id, 'phpunit', 'archive', 1, '/', 'archive.tar.gz');
$this->assertInstanceOf('stored_file', $sf);
// Extract (from storage) to disk.
$outdir = $CFG->tempdir . '/out';
check_dir_exists($outdir);
$packer->extract_to_pathname($sf, $outdir);
// Check the files are as expected.
$this->assertEquals('File 1', file_get_contents($outdir . '/out1.txt'));
$this->assertEquals('File 2', file_get_contents($outdir . '/out2/file2.txt'));
$this->assertEquals('File 3', file_get_contents($outdir . '/out2/dir2/file3.txt'));
$this->assertEquals('File 4', file_get_contents($outdir . '/out3.txt'));
$this->assertEquals('File 5', file_get_contents($outdir . '/out4/file5.txt'));
$this->assertEquals('File 6', file_get_contents($outdir . '/out4/dir2/file6.txt'));
$this->assertEquals('File 7', file_get_contents($outdir . '/out5.txt'));
$this->assertTrue(is_dir($outdir . '/out6'));
// Extract to Moodle storage.
$packer->extract_to_storage($sf, $context->id, 'phpunit', 'data', 2, '/out/');
$fs = get_file_storage();
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/', 'out1.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 1', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out2/', 'file2.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 2', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out2/dir2/', 'file3.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 3', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/', 'out3.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 4', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out4/', 'file5.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 5', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out4/dir2/', 'file6.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 6', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/', 'out5.txt');
$this->assertNotEmpty($out);
$this->assertEquals('File 7', $out->get_content());
$out = $fs->get_file($context->id, 'phpunit', 'data', 2, '/out/out6/', '.');
$this->assertNotEmpty($out);
$this->assertTrue($out->is_directory());
// These functions are supposed to overwrite existing files; test they
// don't give errors when run twice.
$sf = $packer->archive_to_storage($files,
$context->id, 'phpunit', 'archive', 1, '/', 'archive.tar.gz');
$this->assertInstanceOf('stored_file', $sf);
$packer->extract_to_storage($sf, $context->id, 'phpunit', 'data', 2, '/out/');
}
/**
* Tests extracting with a list of specified files.
*/
public function test_only_specified_files(): void {
global $CFG;
$packer = get_file_packer('application/x-gzip');
// Archive files.
$files = $this->prepare_file_list();
$archivefile = $CFG->tempdir . '/test.tar.gz';
$packer->archive_to_pathname($files, $archivefile);
// Extract same files.
$outdir = $CFG->tempdir . '/out';
check_dir_exists($outdir);
$result = $packer->extract_to_pathname($archivefile, $outdir,
array('out3.txt', 'out6/', 'out4/file5.txt'));
// Check result reporting only includes specified files.
$expectedpaths = array('out3.txt', 'out4/file5.txt', 'out6/');
sort($expectedpaths);
$actualpaths = array_keys($result);
sort($actualpaths);
$this->assertEquals($expectedpaths, $actualpaths);
// Check the files are as expected.
$this->assertFalse(file_exists($outdir . '/out1.txt'));
$this->assertEquals('File 4', file_get_contents($outdir . '/out3.txt'));
$this->assertEquals('File 5', file_get_contents($outdir . '/out4/file5.txt'));
$this->assertTrue(is_dir($outdir . '/out6'));
}
/**
* Tests extracting files returning only a boolean state with success.
*/
public function test_extract_to_pathname_returnvalue_successful(): void {
$packer = get_file_packer('application/x-gzip');
// Prepare files.
$files = $this->prepare_file_list();
$archivefile = make_request_directory() . '/test.tgz';
$packer->archive_to_pathname($files, $archivefile);
// Extract same files.
$outdir = make_request_directory();
$result = $packer->extract_to_pathname($archivefile, $outdir, null, null, true);
$this->assertTrue($result);
}
/**
* Tests extracting files returning only a boolean state with failure.
*/
public function test_extract_to_pathname_returnvalue_failure(): void {
$packer = get_file_packer('application/x-gzip');
// Create sample files.
$archivefile = make_request_directory() . '/test.tgz';
file_put_contents($archivefile, '');
// Extract same files.
$outdir = make_request_directory();
$result = $packer->extract_to_pathname($archivefile, $outdir, null, null, true);
$this->assertFalse($result);
}
/**
* Tests the progress reporting.
*/
public function test_file_progress(): void {
global $CFG;
// Set up.
$filelist = $this->prepare_file_list();
$packer = get_file_packer('application/x-gzip');
$archive = "$CFG->tempdir/archive.tgz";
$context = \context_system::instance();
// Archive to pathname.
$this->progress = array();
$result = $packer->archive_to_pathname($filelist, $archive, true, $this);
$this->assertTrue($result);
// Should send progress at least once per file.
$this->assertTrue(count($this->progress) >= count($filelist));
// Progress should obey some restrictions.
$this->check_progress_toward_max();
// Archive to storage.
$this->progress = array();
$archivefile = $packer->archive_to_storage($filelist, $context->id,
'phpunit', 'test', 0, '/', 'archive.tgz', null, true, $this);
$this->assertInstanceOf('stored_file', $archivefile);
$this->assertTrue(count($this->progress) >= count($filelist));
$this->check_progress_toward_max();
// Extract to pathname.
$this->progress = array();
$target = "$CFG->tempdir/test/";
check_dir_exists($target);
$result = $packer->extract_to_pathname($archive, $target, null, $this);
remove_dir($target);
// We only output progress once per block, and this is kind of a small file.
$this->assertTrue(count($this->progress) >= 1);
$this->check_progress_toward_max();
// Extract to storage (from storage).
$this->progress = array();
$result = $packer->extract_to_storage($archivefile, $context->id,
'phpunit', 'target', 0, '/', null, $this);
$this->assertTrue(count($this->progress) >= 1);
$this->check_progress_toward_max();
// Extract to storage (from path).
$this->progress = array();
$result = $packer->extract_to_storage($archive, $context->id,
'phpunit', 'target', 0, '/', null, $this);
$this->assertTrue(count($this->progress) >= 1);
$this->check_progress_toward_max();
// Wipe created disk file.
unlink($archive);
}
/**
* Tests the list_files function with and without an index file.
*/
public function test_list_files(): void {
global $CFG;
// Set up.
$filelist = $this->prepare_file_list();
$packer = get_file_packer('application/x-gzip');
$archive = "$CFG->tempdir/archive.tgz";
// Archive with an index (default).
$packer = get_file_packer('application/x-gzip');
$result = $packer->archive_to_pathname($filelist, $archive, true, $this);
$this->assertTrue($result);
$hashwith = \file_storage::hash_from_path($archive);
// List files.
$files = $packer->list_files($archive);
// Check they match expected.
$expectedinfo = array(
array('out1.txt', 1377993601, false, 6),
array('out2/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
array('out2/dir2/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
array('out2/dir2/file3.txt', 1377993603, false, 6),
array('out2/file2.txt', 1377993602, false, 6),
array('out3.txt', 1377993604, false, 6),
array('out4/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
array('out4/dir2/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
array('out4/dir2/file6.txt', 1377993606, false, 6),
array('out4/file5.txt', 1377993605, false, 6),
array('out5.txt', tgz_packer::DEFAULT_TIMESTAMP, false, 6),
array('out6/', tgz_packer::DEFAULT_TIMESTAMP, true, 0),
);
$this->assertEquals($expectedinfo, self::convert_info_for_assert($files));
// Archive with no index. Should have same result.
$this->progress = array();
$packer->set_include_index(false);
$result = $packer->archive_to_pathname($filelist, $archive, true, $this);
$this->assertTrue($result);
$hashwithout = \file_storage::hash_from_path($archive);
$files = $packer->list_files($archive);
$this->assertEquals($expectedinfo, self::convert_info_for_assert($files));
// Check it actually is different (does have index in)!
$this->assertNotEquals($hashwith, $hashwithout);
// Put the index back on in case of future tests.
$packer->set_include_index(true);
}
/**
* Utility function to convert the file info array into a simpler format
* for making comparisons.
*
* @param array $files Array from list_files result
*/
protected static function convert_info_for_assert(array $files) {
$actualinfo = array();
foreach ($files as $file) {
$actualinfo[] = array($file->pathname, $file->mtime, $file->is_directory, $file->size);
}
usort($actualinfo, function($a, $b) {
return strcmp($a[0], $b[0]);
});
return $actualinfo;
}
public function test_is_tgz_file(): void {
global $CFG;
// Set up.
$filelist = $this->prepare_file_list();
$packer1 = get_file_packer('application/x-gzip');
$packer2 = get_file_packer('application/zip');
$archive2 = "$CFG->tempdir/archive.zip";
// Archive in tgz and zip format.
$context = \context_system::instance();
$archive1 = $packer1->archive_to_storage($filelist, $context->id,
'phpunit', 'test', 0, '/', 'archive.tgz', null, true, $this);
$this->assertInstanceOf('stored_file', $archive1);
$result = $packer2->archive_to_pathname($filelist, $archive2);
$this->assertTrue($result);
// Use is_tgz_file to detect which is which. First check is from storage,
// second check is from filesystem.
$this->assertTrue(tgz_packer::is_tgz_file($archive1));
$this->assertFalse(tgz_packer::is_tgz_file($archive2));
}
/**
* Checks that progress reported is numeric rather than indeterminate,
* and follows the progress reporting rules.
*/
protected function check_progress_toward_max() {
$lastvalue = -1; $lastmax = -1;
foreach ($this->progress as $progressitem) {
list($value, $max) = $progressitem;
if ($lastmax != -1) {
$this->assertEquals($max, $lastmax);
} else {
$lastmax = $max;
}
$this->assertTrue(is_integer($value));
$this->assertTrue(is_integer($max));
$this->assertNotEquals(file_progress::INDETERMINATE, $max);
$this->assertTrue($value <= $max);
$this->assertTrue($value >= $lastvalue);
$lastvalue = $value;
}
}
/**
* Handles file_progress interface.
*
* @param int $progress
* @param int $max
*/
public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
$this->progress[] = array($progress, $max);
}
}
+727
View File
@@ -0,0 +1,727 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core;
use file_archive;
use file_progress;
use zip_archive;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->libdir . '/filestorage/file_progress.php');
/**
* Unit tests for /lib/filestorage/zip_packer.php and zip_archive.php
*
* @package core
* @category test
* @copyright 2012 Petr Skoda
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class zip_packer_test extends \advanced_testcase implements file_progress {
protected $testfile;
protected $files;
/**
* @var array Progress information passed to the progress reporter
*/
protected $progress;
protected function setUp(): void {
parent::setUp();
$this->testfile = __DIR__.'/fixtures/test.txt';
$fs = get_file_storage();
$context = \context_system::instance();
if (!$file = $fs->get_file($context->id, 'phpunit', 'data', 0, '/', 'test.txt')) {
$file = $fs->create_file_from_pathname(
array('contextid'=>$context->id, 'component'=>'phpunit', 'filearea'=>'data', 'itemid'=>0, 'filepath'=>'/', 'filename'=>'test.txt'),
$this->testfile);
}
$this->files = array(
'test.test' => $this->testfile,
'testíček.txt' => $this->testfile,
'Prüfung.txt' => $this->testfile,
'测试.txt' => $this->testfile,
'試験.txt' => $this->testfile,
'Žluťoučký/Koníček.txt' => $file,
);
}
public function test_get_packer(): void {
$this->resetAfterTest(false);
$packer = get_file_packer();
$this->assertInstanceOf('zip_packer', $packer);
$packer = get_file_packer('application/zip');
$this->assertInstanceOf('zip_packer', $packer);
}
/**
* @depends test_get_packer
*/
public function test_list_files(): void {
$this->resetAfterTest(false);
$files = array(
__DIR__.'/fixtures/test_moodle_22.zip',
__DIR__.'/fixtures/test_moodle.zip',
__DIR__.'/fixtures/test_tc_8.zip',
__DIR__.'/fixtures/test_7zip_927.zip',
__DIR__.'/fixtures/test_winzip_165.zip',
__DIR__.'/fixtures/test_winrar_421.zip',
__DIR__.'/fixtures/test_thumbsdb.zip',
);
if (function_exists('normalizer_normalize')) {
// Unfortunately there is no way to standardise UTF-8 strings without INTL extension.
$files[] = __DIR__.'/fixtures/test_infozip_3.zip';
$files[] = __DIR__.'/fixtures/test_osx_1074.zip';
$files[] = __DIR__.'/fixtures/test_osx_compress.zip';
}
$packer = get_file_packer('application/zip');
foreach ($files as $archive) {
$archivefiles = $packer->list_files($archive);
$this->assertTrue(is_array($archivefiles), "Archive not extracted properly: ".basename($archive).' ');
$this->assertTrue(count($this->files) === count($archivefiles) or count($this->files) === count($archivefiles) - 1); // Some zippers create empty dirs.
foreach ($archivefiles as $file) {
if ($file->pathname === 'Žluťoučký/') {
// Some zippers create empty dirs.
continue;
}
$this->assertArrayHasKey($file->pathname, $this->files, "File $file->pathname not extracted properly: ".basename($archive).' ');
}
}
// Windows packer supports only DOS encoding.
$archive = __DIR__.'/fixtures/test_win8_de.zip';
$archivefiles = $packer->list_files($archive);
$this->assertTrue(is_array($archivefiles), "Archive not extracted properly: ".basename($archive).' ');
$this->assertEquals(2, count($archivefiles));
foreach ($archivefiles as $file) {
$this->assertTrue($file->pathname === 'Prüfung.txt' or $file->pathname === 'test.test');
}
$zip_archive = new zip_archive();
$zip_archive->open(__DIR__.'/fixtures/test_win8_cz.zip', file_archive::OPEN, 'cp852');
$archivefiles = $zip_archive->list_files();
$this->assertTrue(is_array($archivefiles), "Archive not extracted properly: ".basename($archive).' ');
$this->assertEquals(3, count($archivefiles));
foreach ($archivefiles as $file) {
$this->assertTrue($file->pathname === 'Žluťoučký/Koníček.txt' or $file->pathname === 'testíček.txt' or $file->pathname === 'test.test');
}
$zip_archive->close();
// Empty archive extraction.
$archive = __DIR__.'/fixtures/empty.zip';
$archivefiles = $packer->list_files($archive);
$this->assertSame(array(), $archivefiles);
}
/**
* @depends test_list_files
*/
public function test_archive_to_pathname(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileDoesNotExist($archive);
$result = $packer->archive_to_pathname($this->files, $archive);
$this->assertTrue($result);
$this->assertFileExists($archive);
$archivefiles = $packer->list_files($archive);
$this->assertTrue(is_array($archivefiles));
$this->assertEquals(count($this->files), count($archivefiles));
foreach ($archivefiles as $file) {
$this->assertArrayHasKey($file->pathname, $this->files);
}
// Test invalid files parameter.
$archive = "$CFG->tempdir/archive2.zip";
$this->assertFileDoesNotExist($archive);
$this->assertFileDoesNotExist(__DIR__.'/xx/yy/ee.txt');
$files = array('xtest.txt'=>__DIR__.'/xx/yy/ee.txt');
$result = $packer->archive_to_pathname($files, $archive, false);
$this->assertFalse($result);
$this->assertDebuggingCalled();
$this->assertFileDoesNotExist($archive);
$result = $packer->archive_to_pathname($files, $archive);
$this->assertTrue($result);
$this->assertFileExists($archive);
$this->assertDebuggingCalled();
$archivefiles = $packer->list_files($archive);
$this->assertSame(array(), $archivefiles);
unlink($archive);
$this->assertFileDoesNotExist(__DIR__.'/xx/yy/ee.txt');
$this->assertFileExists(__DIR__.'/fixtures/test.txt');
$files = array('xtest.txt'=>__DIR__.'/xx/yy/ee.txt', 'test.txt'=>__DIR__.'/fixtures/test.txt', 'ytest.txt'=>__DIR__.'/xx/yy/yy.txt');
$result = $packer->archive_to_pathname($files, $archive);
$this->assertTrue($result);
$this->assertFileExists($archive);
$archivefiles = $packer->list_files($archive);
$this->assertCount(1, $archivefiles);
$this->assertEquals('test.txt', $archivefiles[0]->pathname);
$dms = $this->getDebuggingMessages();
$this->assertCount(2, $dms);
$this->resetDebugging();
unlink($archive);
}
/**
* @depends test_archive_to_pathname
*/
public function test_archive_to_storage(): void {
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$fs = get_file_storage();
$context = \context_system::instance();
$this->assertFalse($fs->file_exists($context->id, 'phpunit', 'test', 0, '/', 'archive.zip'));
$result = $packer->archive_to_storage($this->files, $context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
$this->assertInstanceOf('stored_file', $result);
$this->assertTrue($fs->file_exists($context->id, 'phpunit', 'test', 0, '/', 'archive.zip'));
$archivefiles = $result->list_files($packer);
$this->assertTrue(is_array($archivefiles));
$this->assertEquals(count($this->files), count($archivefiles));
foreach ($archivefiles as $file) {
$this->assertArrayHasKey($file->pathname, $this->files);
}
}
/**
* @depends test_archive_to_storage
*/
public function test_extract_to_pathname(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$fs = get_file_storage();
$context = \context_system::instance();
$target = "$CFG->tempdir/test/";
$testcontent = file_get_contents($this->testfile);
@mkdir($target, $CFG->directorypermissions);
$this->assertTrue(is_dir($target));
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileExists($archive);
$result = $packer->extract_to_pathname($archive, $target);
$this->assertTrue(is_array($result));
$this->assertEquals(count($this->files), count($result));
foreach ($this->files as $file => $unused) {
$this->assertTrue($result[$file]);
$this->assertFileExists($target.$file);
$this->assertSame($testcontent, file_get_contents($target.$file));
}
$archive = $fs->get_file($context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
$this->assertNotEmpty($archive);
$result = $packer->extract_to_pathname($archive, $target);
$this->assertTrue(is_array($result));
$this->assertEquals(count($this->files), count($result));
foreach ($this->files as $file => $unused) {
$this->assertTrue($result[$file]);
$this->assertFileExists($target.$file);
$this->assertSame($testcontent, file_get_contents($target.$file));
}
}
/**
* Test functionality of {@see zip_packer} for entries with folders ending with dots.
*
* @link https://bugs.php.net/bug.php?id=77214
*/
public function test_zip_entry_path_having_folder_ending_with_dot(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$tmp = make_request_directory();
$now = time();
// Create a test archive containing a folder ending with dot.
$zippath = $tmp . '/test_archive.zip';
$zipcontents = [
'HOW.TO' => ['Just run tests.'],
'README.' => ['This is a test ZIP file'],
'./Current time' => [$now],
'Data/sub1./sub2/1221' => ['1221'],
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' => [''],
];
if ($CFG->ostype === 'WINDOWS') {
// File names cannot end with dots on Windows and trailing dots are replaced with underscore.
$filenamemap = [
'HOW.TO' => 'HOW.TO',
'README.' => 'README_',
'./Current time' => 'Current time',
'Data/sub1./sub2/1221' => 'Data/sub1_/sub2/1221',
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' =>
'Data/sub1_/sub2_/Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt',
];
} else {
$filenamemap = [
'HOW.TO' => 'HOW.TO',
'README.' => 'README.',
'./Current time' => 'Current time',
'Data/sub1./sub2/1221' => 'Data/sub1./sub2/1221',
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt' =>
'Data/sub1./sub2./Příliš žluťoučký kůň úpěl Ďábelské Ódy.txt',
];
}
// Check that the archive can be created.
$result = $packer->archive_to_pathname($zipcontents, $zippath, false);
$this->assertTrue($result);
// Check list of files.
$listfiles = $packer->list_files($zippath);
$this->assertEquals(count($zipcontents), count($listfiles));
foreach ($listfiles as $fileinfo) {
$this->assertSame($fileinfo->pathname, $fileinfo->original_pathname);
$this->assertArrayHasKey($fileinfo->pathname, $zipcontents);
}
// Check actual extracting.
$targetpath = $tmp . '/target';
check_dir_exists($targetpath);
$result = $packer->extract_to_pathname($zippath, $targetpath, null, null, true);
$this->assertTrue($result);
foreach ($zipcontents as $filename => $filecontents) {
$filecontents = reset($filecontents);
$this->assertTrue(is_readable($targetpath . '/' . $filenamemap[$filename]));
$this->assertEquals($filecontents, file_get_contents($targetpath . '/' . $filenamemap[$filename]));
}
}
/**
* @depends test_archive_to_storage
*/
public function test_extract_to_pathname_onlyfiles(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$fs = get_file_storage();
$context = \context_system::instance();
$target = "$CFG->tempdir/onlyfiles/";
$testcontent = file_get_contents($this->testfile);
@mkdir($target, $CFG->directorypermissions);
$this->assertTrue(is_dir($target));
$onlyfiles = array('test', 'test.test', 'Žluťoučký/Koníček.txt', 'Idontexist');
$willbeextracted = array_intersect(array_keys($this->files), $onlyfiles);
$donotextract = array_diff(array_keys($this->files), $onlyfiles);
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileExists($archive);
$result = $packer->extract_to_pathname($archive, $target, $onlyfiles);
$this->assertTrue(is_array($result));
$this->assertEquals(count($willbeextracted), count($result));
foreach ($willbeextracted as $file) {
$this->assertTrue($result[$file]);
$this->assertFileExists($target.$file);
$this->assertSame($testcontent, file_get_contents($target.$file));
}
foreach ($donotextract as $file) {
$this->assertFalse(isset($result[$file]));
$this->assertFileDoesNotExist($target.$file);
}
}
/**
* @depends test_archive_to_storage
*/
public function test_extract_to_pathname_returnvalue_successful(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$target = make_request_directory();
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileExists($archive);
$result = $packer->extract_to_pathname($archive, $target, null, null, true);
$this->assertTrue($result);
}
/**
* @depends test_archive_to_storage
*/
public function test_extract_to_pathname_returnvalue_failure(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$target = make_request_directory();
$archive = "$CFG->tempdir/noarchive.zip";
$result = $packer->extract_to_pathname($archive, $target, null, null, true);
$this->assertFalse($result);
}
/**
* @depends test_archive_to_storage
*/
public function test_extract_to_storage(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$fs = get_file_storage();
$context = \context_system::instance();
$testcontent = file_get_contents($this->testfile);
$archive = $fs->get_file($context->id, 'phpunit', 'test', 0, '/', 'archive.zip');
$this->assertNotEmpty($archive);
$result = $packer->extract_to_storage($archive, $context->id, 'phpunit', 'target', 0, '/');
$this->assertTrue(is_array($result));
$this->assertEquals(count($this->files), count($result));
foreach ($this->files as $file => $unused) {
$this->assertTrue($result[$file]);
$stored_file = $fs->get_file_by_hash(sha1("/$context->id/phpunit/target/0/$file"));
$this->assertInstanceOf('stored_file', $stored_file);
$this->assertSame($testcontent, $stored_file->get_content());
}
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileExists($archive);
$result = $packer->extract_to_storage($archive, $context->id, 'phpunit', 'target', 0, '/');
$this->assertTrue(is_array($result));
$this->assertEquals(count($this->files), count($result));
foreach ($this->files as $file => $unused) {
$this->assertTrue($result[$file]);
$stored_file = $fs->get_file_by_hash(sha1("/$context->id/phpunit/target/0/$file"));
$this->assertInstanceOf('stored_file', $stored_file);
$this->assertSame($testcontent, $stored_file->get_content());
}
unlink($archive);
}
/**
* @depends test_extract_to_storage
*/
public function test_add_files(): void {
global $CFG;
$this->resetAfterTest(false);
$packer = get_file_packer('application/zip');
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileDoesNotExist($archive);
$packer->archive_to_pathname(array(), $archive);
$this->assertFileExists($archive);
$zip_archive = new zip_archive();
$zip_archive->open($archive, file_archive::OPEN);
$this->assertEquals(0, $zip_archive->count());
$zip_archive->add_file_from_string('test.txt', 'test');
$zip_archive->close();
$zip_archive->open($archive, file_archive::OPEN);
$this->assertEquals(1, $zip_archive->count());
$zip_archive->add_directory('test2');
$zip_archive->close();
$zip_archive->open($archive, file_archive::OPEN);
$files = $zip_archive->list_files();
$this->assertCount(2, $files);
$this->assertEquals('test.txt', $files[0]->pathname);
$this->assertEquals('test2/', $files[1]->pathname);
$result = $zip_archive->add_file_from_pathname('test.txt', __DIR__.'/nonexistent/file.txt');
$this->assertFalse($result);
$zip_archive->close();
$zip_archive->open($archive, file_archive::OPEN);
$this->assertEquals(2, $zip_archive->count());
$zip_archive->close();
unlink($archive);
}
public function test_close_archive(): void {
global $CFG;
$this->resetAfterTest(true);
$archive = "$CFG->tempdir/archive.zip";
$textfile = "$CFG->tempdir/textfile.txt";
touch($textfile);
$this->assertFileDoesNotExist($archive);
$this->assertFileExists($textfile);
// Create archive and close it without files.
// (returns true, without any warning).
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::CREATE);
$this->assertTrue($result);
$result = $zip_archive->close();
$this->assertTrue($result);
unlink($archive);
// Create archive and close it with files.
// (returns true, without any warning).
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::CREATE);
$this->assertTrue($result);
$result = $zip_archive->add_file_from_string('test.txt', 'test');
$this->assertTrue($result);
$result = $zip_archive->add_file_from_pathname('test2.txt', $textfile);
$result = $zip_archive->close();
$this->assertTrue($result);
unlink($archive);
// Create archive and close if forcing error.
// (returns true for old PHP versions and
// false with warnings for new PHP versions). MDL-51863.
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::CREATE);
$this->assertTrue($result);
$result = $zip_archive->add_file_from_string('test.txt', 'test');
$this->assertTrue($result);
$result = $zip_archive->add_file_from_pathname('test2.txt', $textfile);
$this->assertTrue($result);
// Delete the file before closing does force close() to fail.
unlink($textfile);
// Behavior is different between old PHP versions and new ones. Let's detect it.
$result = false;
try {
// Old PHP versions were not printing any warning.
$result = $zip_archive->close();
} catch (\Exception $e) {
// New PHP versions print PHP Warning.
$this->assertInstanceOf('PHPUnit\Framework\Error\Warning', $e);
$this->assertStringContainsString('ZipArchive::close', $e->getMessage());
}
// This is crazy, but it shows how some PHP versions do return true.
try {
// And some PHP versions do return correctly false (5.4.25, 5.6.14...)
$this->assertFalse($result);
} catch (\Exception $e) {
// But others do insist into returning true (5.6.13...). Only can accept them.
$this->assertInstanceOf('PHPUnit\Framework\ExpectationFailedException', $e);
$this->assertTrue($result);
}
$this->assertFileDoesNotExist($archive);
}
/**
* @depends test_add_files
*/
public function test_open_archive(): void {
global $CFG;
$this->resetAfterTest(true);
$archive = "$CFG->tempdir/archive.zip";
$this->assertFileDoesNotExist($archive);
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::OPEN);
$this->assertFalse($result);
$this->assertDebuggingCalled();
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::CREATE);
$this->assertTrue($result);
$zip_archive->add_file_from_string('test.txt', 'test');
$zip_archive->close();
$zip_archive->open($archive, file_archive::OPEN);
$this->assertEquals(1, $zip_archive->count());
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::OVERWRITE);
$this->assertTrue($result);
$zip_archive->add_file_from_string('test2.txt', 'test');
$zip_archive->close();
$zip_archive->open($archive, file_archive::OPEN);
$this->assertEquals(1, $zip_archive->count());
$zip_archive->close();
unlink($archive);
$zip_archive = new zip_archive();
$result = $zip_archive->open($archive, file_archive::OVERWRITE);
$this->assertTrue($result);
$zip_archive->add_file_from_string('test2.txt', 'test');
$zip_archive->close();
$zip_archive->open($archive, file_archive::OPEN);
$this->assertEquals(1, $zip_archive->count());
$zip_archive->close();
unlink($archive);
}
/**
* Test opening an encrypted archive
*/
public function test_open_encrypted_archive(): void {
$this->resetAfterTest();
// The archive contains a single encrypted "hello.txt" file.
$archive = __DIR__ . '/fixtures/passwordis1.zip';
/** @var \zip_packer $packer */
$packer = get_file_packer('application/zip');
$result = $packer->extract_to_pathname($archive, make_temp_directory('zip'));
$this->assertIsArray($result);
$this->assertArrayHasKey('hello.txt', $result);
$this->assertEquals('Can not read file from zip archive', $result['hello.txt']);
}
/**
* Tests the progress reporting.
*/
public function test_file_progress(): void {
global $CFG;
// Set up.
$this->resetAfterTest(true);
$packer = get_file_packer('application/zip');
$archive = "$CFG->tempdir/archive.zip";
$context = \context_system::instance();
// Archive to pathname.
$this->progress = array();
$result = $packer->archive_to_pathname($this->files, $archive, true, $this);
$this->assertTrue($result);
// Should send progress at least once per file.
$this->assertTrue(count($this->progress) >= count($this->files));
// Each progress will be indeterminate.
$this->assertEquals(
array(file_progress::INDETERMINATE, file_progress::INDETERMINATE),
$this->progress[0]);
// Archive to pathname using entire folder and subfolder instead of file list.
unlink($archive);
$folder = make_temp_directory('zip_packer_progress');
file_put_contents($folder . '/test1.txt', 'hello');
$subfolder = $folder . '/sub';
check_dir_exists($subfolder);
file_put_contents($subfolder . '/test2.txt', 'world');
file_put_contents($subfolder . '/test3.txt', 'and');
file_put_contents($subfolder . '/test4.txt', 'other');
file_put_contents($subfolder . '/test5.txt', 'worlds');
$this->progress = array();
$result = $packer->archive_to_pathname(array('' => $folder), $archive, true, $this);
$this->assertTrue($result);
// Should send progress at least once per file.
$this->assertTrue(count($this->progress) >= 5);
// Archive to storage.
$this->progress = array();
$archivefile = $packer->archive_to_storage($this->files, $context->id,
'phpunit', 'test', 0, '/', 'archive.zip', null, true, $this);
$this->assertInstanceOf('stored_file', $archivefile);
$this->assertTrue(count($this->progress) >= count($this->files));
$this->assertEquals(
array(file_progress::INDETERMINATE, file_progress::INDETERMINATE),
$this->progress[0]);
// Extract to pathname.
$this->progress = array();
$target = "$CFG->tempdir/test/";
check_dir_exists($target);
$result = $packer->extract_to_pathname($archive, $target, null, $this);
remove_dir($target);
$this->assertEquals(count($this->files), count($result));
$this->assertTrue(count($this->progress) >= count($this->files));
$this->check_progress_toward_max();
// Extract to storage (from storage).
$this->progress = array();
$result = $packer->extract_to_storage($archivefile, $context->id,
'phpunit', 'target', 0, '/', null, $this);
$this->assertEquals(count($this->files), count($result));
$this->assertTrue(count($this->progress) >= count($this->files));
$this->check_progress_toward_max();
// Extract to storage (from path).
$this->progress = array();
$result = $packer->extract_to_storage($archive, $context->id,
'phpunit', 'target', 0, '/', null, $this);
$this->assertEquals(count($this->files), count($result));
$this->assertTrue(count($this->progress) >= count($this->files));
$this->check_progress_toward_max();
// Wipe created disk file.
unlink($archive);
}
/**
* Checks that progress reported is numeric rather than indeterminate,
* and follows the progress reporting rules.
*/
private function check_progress_toward_max() {
$lastvalue = -1;
foreach ($this->progress as $progressitem) {
list($value, $max) = $progressitem;
$this->assertNotEquals(file_progress::INDETERMINATE, $max);
$this->assertTrue($value <= $max);
$this->assertTrue($value >= $lastvalue);
$lastvalue = $value;
}
}
/**
* Handles file_progress interface.
*
* @param int $progress
* @param int $max
*/
public function progress($progress = file_progress::INDETERMINATE, $max = file_progress::INDETERMINATE) {
$this->progress[] = array($progress, $max);
}
}