mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Enhanced tests for regeneration of images and a bugfix for images that were cached in multiple steps
This commit is contained in:
parent
f8ee3e1b73
commit
5fbb3da661
@ -151,8 +151,8 @@ class HtmlEditorField extends TextareaField {
|
||||
}
|
||||
|
||||
// Resample the images if the width & height have changed.
|
||||
$width = $img->getAttribute('width');
|
||||
$height = $img->getAttribute('height');
|
||||
$width = (int)$img->getAttribute('width');
|
||||
$height = (int)$img->getAttribute('height');
|
||||
|
||||
if($image){
|
||||
if($width && $height && ($width != $image->getWidth() || $height != $image->getHeight())) {
|
||||
|
@ -428,17 +428,23 @@ class Image extends File {
|
||||
* Return the filename for the cached image, given it's format name and arguments.
|
||||
* @param string $format The format name.
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function cacheFilename($format) {
|
||||
$args = func_get_args();
|
||||
array_shift($args);
|
||||
$folder = $this->ParentID ? $this->Parent()->Filename : ASSETS_DIR . "/";
|
||||
|
||||
$format = $format.implode('x', $args);
|
||||
$format = $format . base64_encode(json_encode($args));
|
||||
$filename = $format . "-" . $this->Name;
|
||||
$patterns = $this->getFilenamePatterns($this->Name);
|
||||
if (!preg_match($patterns['FullPattern'], $filename)) {
|
||||
throw new InvalidArgumentException('Filename ' . $filename . ' that should be used to cache a resized image is invalid');
|
||||
}
|
||||
|
||||
return $folder . "_resampled/$format-" . $this->Name;
|
||||
return $folder . "_resampled/" . $filename;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate an image on the specified format. It will save the image
|
||||
* at the location specified by cacheFilename(). The image will be generated
|
||||
@ -532,14 +538,36 @@ class Image extends File {
|
||||
public function generateCroppedImage(Image_Backend $backend, $width, $height) {
|
||||
return $backend->croppedResize($width, $height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate patterns that will help to match filenames of cached images
|
||||
*
|
||||
* @param string $filename Filename of source image
|
||||
* @return array
|
||||
*/
|
||||
private function getFilenamePatterns($filename) {
|
||||
$methodNames = $this->allMethodNames(true);
|
||||
$generateFuncs = array();
|
||||
foreach($methodNames as $methodName) {
|
||||
if(substr($methodName, 0, 8) == 'generate') {
|
||||
$format = substr($methodName, 8);
|
||||
$generateFuncs[] = preg_quote($format);
|
||||
}
|
||||
}
|
||||
// All generate functions may appear any number of times in the image cache name.
|
||||
$generateFuncs = implode('|', $generateFuncs);
|
||||
$base64Match = "[a-zA-Z0-9\/\r\n+]*={0,2}";
|
||||
return array(
|
||||
'FullPattern' => "/^((?P<Generator>{$generateFuncs})(?P<Args>" . $base64Match . ")\-)+" . preg_quote($filename) . "$/i",
|
||||
'GeneratorPattern' => "/(?P<Generator>{$generateFuncs})(?P<Args>" . $base64Match . ")\-/i"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a list of images that were generated from this image
|
||||
*/
|
||||
private function getGeneratedImages() {
|
||||
$generatedImages = array();
|
||||
|
||||
$methodNames = $this->allMethodNames(true);
|
||||
$cachedFiles = array();
|
||||
|
||||
$folder = $this->ParentID ? $this->Parent()->Filename : ASSETS_DIR . '/';
|
||||
@ -557,22 +585,26 @@ class Image extends File {
|
||||
}
|
||||
}
|
||||
|
||||
$generateFuncs = array();
|
||||
foreach($methodNames as $methodName) {
|
||||
if(substr($methodName, 0, 8) == 'generate') {
|
||||
$format = substr($methodName, 8);
|
||||
$generateFuncs[] = preg_quote($format);
|
||||
}
|
||||
}
|
||||
// All generate functions may appear any number of times in the image cache name.
|
||||
$generateFuncs = implode('|', $generateFuncs);
|
||||
$pattern = "/^((?P<Generator>{$generateFuncs})(?P<Arg1>\d*)x(?P<Arg2>\d*)\-)+" . preg_quote($this->Name) . "$/i";
|
||||
$pattern = $this->getFilenamePatterns($this->Name);
|
||||
|
||||
foreach($cachedFiles as $cfile) {
|
||||
if(preg_match($pattern, $cfile, $matches)) {
|
||||
if(preg_match($pattern['FullPattern'], $cfile, $matches)) {
|
||||
if(Director::fileExists($cacheDir . $cfile)) {
|
||||
$generatedImages[] = array ( 'FileName' => $cacheDir . $cfile, 'Generator' => $matches['Generator'],
|
||||
'Arg1' => $matches['Arg1'], 'Arg2' => $matches['Arg2'] );
|
||||
$subFilename = substr($cfile, 0, -1 * strlen($this->Name));
|
||||
preg_match_all($pattern['GeneratorPattern'], $subFilename, $subMatches, PREG_SET_ORDER);
|
||||
|
||||
$generatorArray = array();
|
||||
foreach ($subMatches as $singleMatch) {
|
||||
$generatorArray[] = array('Generator' => $singleMatch['Generator'],
|
||||
'Args' => json_decode(base64_decode($singleMatch['Args'])));
|
||||
}
|
||||
|
||||
// Using array_reverse is important, as a cached image will
|
||||
// have the generators settings in the filename in reversed
|
||||
// order: the last generator given in the filename is the
|
||||
// first that was used. Later resizements are prepended
|
||||
$generatedImages[] = array ( 'FileName' => $cacheDir . $cfile,
|
||||
'Generators' => array_reverse($generatorArray) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,11 +620,23 @@ class Image extends File {
|
||||
public function regenerateFormattedImages() {
|
||||
if(!$this->Filename) return 0;
|
||||
|
||||
// Without this, not a single file would be written
|
||||
// caused by a check in getFormattedImage()
|
||||
$_GET['flush'] = 1;
|
||||
|
||||
$numGenerated = 0;
|
||||
$generatedImages = $this->getGeneratedImages();
|
||||
$doneList = array();
|
||||
foreach($generatedImages as $singleImage) {
|
||||
$this->generateFormattedImage($singleImage['Generator'], $singleImage['Arg1'],
|
||||
$singleImage['Arg2']);
|
||||
$cachedImage = $this;
|
||||
if (in_array($singleImage['FileName'], $doneList) ) continue;
|
||||
|
||||
foreach($singleImage['Generators'] as $singleGenerator) {
|
||||
$args = array_merge(array($singleGenerator['Generator']), $singleGenerator['Args']);
|
||||
$cachedImage = call_user_func_array(array($cachedImage, "getFormattedImage"), $args);
|
||||
}
|
||||
$doneList[] = $singleImage['FileName'];
|
||||
$numGenerated++;
|
||||
}
|
||||
|
||||
return $numGenerated;
|
||||
|
@ -87,8 +87,11 @@ class HtmlEditorFieldTest extends FunctionalTest {
|
||||
$this->assertEquals('', (string)$xml[0]['title'], 'Title tags are added by default.');
|
||||
$this->assertEquals(10, (int)$xml[0]['width'], 'Width tag of resized image is set.');
|
||||
$this->assertEquals(20, (int)$xml[0]['height'], 'Height tag of resized image is set.');
|
||||
$this->assertEquals('assets/_resampled/ResizedImage10x20-HTMLEditorFieldTest_example.jpg', (string)$xml[0]['src'], 'Correct URL of resized image is set.');
|
||||
$this->assertTrue(file_exists('assets/_resampled/ResizedImage10x20-HTMLEditorFieldTest_example.jpg'), 'File for resized image exists');
|
||||
|
||||
$neededFilename = 'assets/_resampled/ResizedImage' . base64_encode(json_encode(array(10,20))) . '-HTMLEditorFieldTest_example.jpg';
|
||||
|
||||
$this->assertEquals($neededFilename, (string)$xml[0]['src'], 'Correct URL of resized image is set.');
|
||||
$this->assertTrue(file_exists($neededFilename), 'File for resized image exists');
|
||||
$this->assertEquals(false, $obj->HasBrokenFile, 'Referenced image file exists.');
|
||||
}
|
||||
|
||||
|
@ -175,6 +175,90 @@ class ImageTest extends SapphireTest {
|
||||
$ratio = $image->SetRatioSize(80, 160);
|
||||
$this->assertTrue($ratio->isSize(80, 80));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException PHPUnit_Framework_Error
|
||||
*/
|
||||
public function testGenerateImageWithInvalidParameters() {
|
||||
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
|
||||
$image->setHeight('String');
|
||||
$image->PaddedImage(600,600,'XXXXXX');
|
||||
}
|
||||
|
||||
public function testCacheFilename() {
|
||||
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
|
||||
$imageFirst = $image->SetSize(200,200);
|
||||
$imageFilename = $imageFirst->getFullPath();
|
||||
// Encoding of the arguments is duplicated from cacheFilename
|
||||
$neededPart = 'SetSize' . base64_encode(json_encode(array(200,200)));
|
||||
$this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated');
|
||||
}
|
||||
|
||||
public function testMultipleGenerateManipulationCalls_Regeneration() {
|
||||
$image = $this->objFromFixture('Image', 'imageWithoutTitle');
|
||||
$folder = new SS_FileFinder();
|
||||
|
||||
$imageFirst = $image->SetSize(200,200);
|
||||
$this->assertNotNull($imageFirst);
|
||||
$expected = 200;
|
||||
$actual = $imageFirst->getWidth();
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
|
||||
$imageSecond = $imageFirst->setHeight(100);
|
||||
$this->assertNotNull($imageSecond);
|
||||
$expected = 100;
|
||||
$actual = $imageSecond->getHeight();
|
||||
$this->assertEquals($expected, $actual);
|
||||
|
||||
$imageThird = $imageSecond->PaddedImage(600,600,'0F0F0F');
|
||||
// Encoding of the arguments is duplicated from cacheFilename
|
||||
$argumentString = base64_encode(json_encode(array(600,600,'0F0F0F')));
|
||||
$this->assertNotNull($imageThird);
|
||||
$this->assertContains($argumentString, $imageThird->getFullPath(), 'Image contains background color for padded resizement');
|
||||
|
||||
$imageThirdPath = $imageThird->getFullPath();
|
||||
$filesInFolder = $folder->find(dirname($imageThirdPath));
|
||||
$this->assertEquals(3, count($filesInFolder), 'Image folder contains only the expected number of images before regeneration');
|
||||
|
||||
$hash = md5_file($imageThirdPath);
|
||||
$this->assertEquals(3, $image->regenerateFormattedImages(), 'Cached images were regenerated in the right number');
|
||||
$this->assertEquals($hash, md5_file($imageThirdPath), 'Regeneration of third image is correct');
|
||||
|
||||
/* Check that no other images exist, to ensure that the regeneration did not create other images */
|
||||
$this->assertEquals($filesInFolder, $folder->find(dirname($imageThirdPath)), 'Image folder contains only the expected image files after regeneration');
|
||||
}
|
||||
|
||||
public function testRegenerateImages() {
|
||||
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
|
||||
$image_generated = $image->SetWidth(200);
|
||||
$p = $image_generated->getFullPath();
|
||||
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
|
||||
$this->assertEquals(1, $image->regenerateFormattedImages(), 'Cached images were regenerated correct');
|
||||
$this->assertEquals($image_generated->getWidth(), 200, 'Resized image has correct width after regeneration call');
|
||||
$this->assertTrue(file_exists($p), 'Resized image exists after regeneration call');
|
||||
}
|
||||
|
||||
public function testRegenerateImagesWithRenaming() {
|
||||
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
|
||||
$image_generated = $image->SetWidth(200);
|
||||
$p = $image_generated->getFullPath();
|
||||
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
|
||||
|
||||
// Encoding of the arguments is duplicated from cacheFilename
|
||||
$oldArgumentString = base64_encode(json_encode(array(200)));
|
||||
$newArgumentString = base64_encode(json_encode(array(300)));
|
||||
|
||||
$newPath = str_replace($oldArgumentString, $newArgumentString, $p);
|
||||
$newRelative = str_replace($oldArgumentString, $newArgumentString, $image_generated->getFileName());
|
||||
rename($p, $newPath);
|
||||
$this->assertFalse(file_exists($p), 'Resized image does not exist after movement call under old name');
|
||||
$this->assertTrue(file_exists($newPath), 'Resized image exists after movement call under new name');
|
||||
$this->assertEquals(1, $image->regenerateFormattedImages(), 'Cached images were regenerated in the right number');
|
||||
|
||||
$image_generated_2 = new Image_Cached($newRelative);
|
||||
$this->assertEquals(300, $image_generated_2->getWidth(), 'Cached image was regenerated with correct width');
|
||||
}
|
||||
|
||||
public function testGeneratedImageDeletion() {
|
||||
$image = $this->objFromFixture('Image', 'imageWithMetacharacters');
|
||||
|
Loading…
x
Reference in New Issue
Block a user