Skip to content

Commit

Permalink
Merge branch 'MDL-55243-311' of git://github.com/HuongNV13/moodle int…
Browse files Browse the repository at this point in the history
…o MOODLE_311_STABLE
  • Loading branch information
andrewnicols committed Jun 24, 2021
2 parents 2f07a94 + 29f384f commit 4e9bef0
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 12 deletions.
18 changes: 17 additions & 1 deletion lib/filelib.php
Original file line number Diff line number Diff line change
Expand Up @@ -2529,6 +2529,12 @@ function send_file($path, $filename, $lifetime = null , $filter=0, $pathisstring
$filename = rawurlencode($filename);
}

// We need to force download and force filter the file content for the SVG file.
if (file_is_svg_image_from_mimetype($mimetype)) {
$forcedownload = true;
$filter = 1;
}

if ($forcedownload) {
header('Content-Disposition: attachment; filename="'.$filename.'"');

Expand Down Expand Up @@ -2589,7 +2595,7 @@ function send_file($path, $filename, $lifetime = null , $filter=0, $pathisstring

} else {
// Try to put the file through filters
if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml') {
if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml' || file_is_svg_image_from_mimetype($mimetype)) {
$options = new stdClass();
$options->noclean = true;
$options->nocache = true; // temporary workaround for MDL-5136
Expand Down Expand Up @@ -3018,6 +3024,16 @@ function file_merge_draft_area_into_draft_area($getfromdraftid, $mergeintodrafti
}
}

/**
* Attempt to determine whether the specified mime-type is an SVG image or not.
*
* @param string $mimetype Mime-type
* @return bool True if it is an SVG file
*/
function file_is_svg_image_from_mimetype(string $mimetype): bool {
return preg_match('|^image/svg|', $mimetype);
}

/**
* RESTful cURL class
*
Expand Down
61 changes: 50 additions & 11 deletions lib/filestorage/file_system.php
Original file line number Diff line number Diff line change
Expand Up @@ -398,21 +398,60 @@ public function is_image_from_storedfile(stored_file $file) {
/**
* Returns image information relating to the specified path or URL.
*
* @param string $path The path to pass to getimagesize.
* @return array Containing width, height, and mimetype.
* @param string $path The full path of the image file.
* @return array|bool array that containing width, height, and mimetype or false if cannot get the image info.
*/
protected function get_imageinfo_from_path($path) {
$imageinfo = getimagesize($path);
$imagemimetype = file_storage::mimetype_from_file($path);
$issvgimage = file_is_svg_image_from_mimetype($imagemimetype);

if (!is_array($imageinfo)) {
return false; // Nothing to process, the file was not recognised as image by GD.
}
if (!$issvgimage) {
$imageinfo = getimagesize($path);
if (!is_array($imageinfo)) {
return false; // Nothing to process, the file was not recognised as image by GD.
}
$image = [
'width' => $imageinfo[0],
'height' => $imageinfo[1],
'mimetype' => image_type_to_mime_type($imageinfo[2]),
];
} else {
// Since SVG file is actually an XML file, GD cannot handle.
$svgcontent = @simplexml_load_file($path);
if (!$svgcontent) {
// Cannot parse the file.
return false;
}
$svgattrs = $svgcontent->attributes();

if (!empty($svgattrs->viewBox)) {
// We have viewBox.
$viewboxval = explode(' ', $svgattrs->viewBox);
$width = intval($viewboxval[2]);
$height = intval($viewboxval[3]);
} else {
// Get the width.
if (!empty($svgattrs->width) && intval($svgattrs->width) > 0) {
$width = intval($svgattrs->width);
} else {
// Default width.
$width = 800;
}
// Get the height.
if (!empty($svgattrs->height) && intval($svgattrs->height) > 0) {
$height = intval($svgattrs->height);
} else {
// Default width.
$height = 600;
}
}

$image = array(
'width' => $imageinfo[0],
'height' => $imageinfo[1],
'mimetype' => image_type_to_mime_type($imageinfo[2]),
);
$image = [
'width' => $width,
'height' => $height,
'mimetype' => $imagemimetype,
];
}

if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) {
// GD can not parse it, sorry.
Expand Down
83 changes: 83 additions & 0 deletions lib/filestorage/tests/file_system_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,89 @@ public function test_get_imageinfo_from_path_no_image() {
$this->assertFalse($result);
}

/**
* Test that get_imageinfo_from_path returns an appropriate response
* for an svg image with viewbox attribute.
*/
public function test_get_imageinfo_from_path_svg_viewbox() {
$filepath = __DIR__ . '/fixtures/testimage_viewbox.svg';

// Get the filesystem mock.
$fs = $this->get_testable_mock();

$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
$method->setAccessible(true);
$result = $method->invokeArgs($fs, [$filepath]);

$this->assertArrayHasKey('width', $result);
$this->assertArrayHasKey('height', $result);
$this->assertArrayHasKey('mimetype', $result);
$this->assertEquals(100, $result['width']);
$this->assertEquals(100, $result['height']);
$this->assertStringContainsString('image/svg', $result['mimetype']);
}

/**
* Test that get_imageinfo_from_path returns an appropriate response
* for an svg image with width and height attributes.
*/
public function test_get_imageinfo_from_path_svg_with_width_height() {
$filepath = __DIR__ . '/fixtures/testimage_width_height.svg';

// Get the filesystem mock.
$fs = $this->get_testable_mock();

$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
$method->setAccessible(true);
$result = $method->invokeArgs($fs, [$filepath]);

$this->assertArrayHasKey('width', $result);
$this->assertArrayHasKey('height', $result);
$this->assertArrayHasKey('mimetype', $result);
$this->assertEquals(100, $result['width']);
$this->assertEquals(100, $result['height']);
$this->assertStringContainsString('image/svg', $result['mimetype']);
}

/**
* Test that get_imageinfo_from_path returns an appropriate response
* for an svg image without attributes.
*/
public function test_get_imageinfo_from_path_svg_without_attribute() {
$filepath = __DIR__ . '/fixtures/testimage.svg';

// Get the filesystem mock.
$fs = $this->get_testable_mock();

$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
$method->setAccessible(true);
$result = $method->invokeArgs($fs, [$filepath]);

$this->assertArrayHasKey('width', $result);
$this->assertArrayHasKey('height', $result);
$this->assertArrayHasKey('mimetype', $result);
$this->assertEquals(800, $result['width']);
$this->assertEquals(600, $result['height']);
$this->assertStringContainsString('image/svg', $result['mimetype']);
}

/**
* Test that get_imageinfo_from_path returns an appropriate response
* for a file which is not an correct svg.
*/
public function test_get_imageinfo_from_path_svg_invalid() {
$filepath = __DIR__ . '/fixtures/testimage_error.svg';

// Get the filesystem mock.
$fs = $this->get_testable_mock();

$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
$method->setAccessible(true);
$result = $method->invokeArgs($fs, [$filepath]);

$this->assertFalse($result);
}

/**
* Ensure that get_content_file_handle returns a valid file handle.
*
Expand Down
14 changes: 14 additions & 0 deletions lib/filestorage/tests/fixtures/testimage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions lib/filestorage/tests/fixtures/testimage_error.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions lib/filestorage/tests/fixtures/testimage_viewbox.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions lib/filestorage/tests/fixtures/testimage_width_height.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4e9bef0

Please sign in to comment.