mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Url-safe alternative for base64_encode in resampled Image filenames
This commit is contained in:
parent
c895797ccf
commit
18e163d985
@ -290,8 +290,8 @@ class Convert {
|
|||||||
/**
|
/**
|
||||||
* Create a link if the string is a valid URL
|
* Create a link if the string is a valid URL
|
||||||
*
|
*
|
||||||
* @param string The string to linkify
|
* @param string $string The string to linkify
|
||||||
* @return A link to the URL if string is a URL
|
* @return string A link to the URL if string is a URL
|
||||||
*/
|
*/
|
||||||
public static function linkIfMatch($string) {
|
public static function linkIfMatch($string) {
|
||||||
if( preg_match( '/^[a-z+]+\:\/\/[a-zA-Z0-9$-_.+?&=!*\'()%]+$/', $string ) )
|
if( preg_match( '/^[a-z+]+\:\/\/[a-zA-Z0-9$-_.+?&=!*\'()%]+$/', $string ) )
|
||||||
@ -305,7 +305,9 @@ class Convert {
|
|||||||
*
|
*
|
||||||
* @param string $data Input data
|
* @param string $data Input data
|
||||||
* @param bool $preserveLinks
|
* @param bool $preserveLinks
|
||||||
* @param int $wordwrap
|
* @param int $wordWrap
|
||||||
|
* @param array $config
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function html2raw($data, $preserveLinks = false, $wordWrap = 0, $config = null) {
|
public static function html2raw($data, $preserveLinks = false, $wordWrap = 0, $config = null) {
|
||||||
$defaultConfig = array(
|
$defaultConfig = array(
|
||||||
@ -414,8 +416,33 @@ class Convert {
|
|||||||
* sequences including \r, \r\n, \n, or unicode newline characters
|
* sequences including \r, \r\n, \n, or unicode newline characters
|
||||||
* @param string $nl The newline sequence to normalise to. Defaults to that
|
* @param string $nl The newline sequence to normalise to. Defaults to that
|
||||||
* specified by the current OS
|
* specified by the current OS
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function nl2os($data, $nl = PHP_EOL) {
|
public static function nl2os($data, $nl = PHP_EOL) {
|
||||||
return preg_replace('~\R~u', $nl, $data);
|
return preg_replace('~\R~u', $nl, $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a value into a string that can be used as part of a filename.
|
||||||
|
* All string data must be UTF-8 encoded.
|
||||||
|
*
|
||||||
|
* @param mixed $val Value to be encoded
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function base64url_encode($val) {
|
||||||
|
return rtrim(strtr(base64_encode(json_encode($val)), '+/', '~_'), '=');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a value that was encoded with Convert::base64url_encode.
|
||||||
|
*
|
||||||
|
* @param string $val Value to be decoded
|
||||||
|
* @return mixed Original value
|
||||||
|
*/
|
||||||
|
public static function base64url_decode($val) {
|
||||||
|
return json_decode(
|
||||||
|
base64_decode(str_pad(strtr($val, '~_', '+/'), strlen($val) % 4, '=', STR_PAD_RIGHT)),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -701,7 +701,7 @@ class Image extends File implements Flushable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the filename for the cached image, given it's format name and arguments.
|
* Return the filename for the cached image, given its format name and arguments.
|
||||||
* @param string $format The format name.
|
* @param string $format The format name.
|
||||||
* @return string
|
* @return string
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
@ -711,7 +711,7 @@ class Image extends File implements Flushable {
|
|||||||
array_shift($args);
|
array_shift($args);
|
||||||
$folder = $this->ParentID ? $this->Parent()->Filename : ASSETS_DIR . "/";
|
$folder = $this->ParentID ? $this->Parent()->Filename : ASSETS_DIR . "/";
|
||||||
|
|
||||||
$format = $format . base64_encode(json_encode($args, JSON_NUMERIC_CHECK));
|
$format = $format . Convert::base64url_encode($args);
|
||||||
$filename = $format . "-" . $this->Name;
|
$filename = $format . "-" . $this->Name;
|
||||||
$patterns = $this->getFilenamePatterns($this->Name);
|
$patterns = $this->getFilenamePatterns($this->Name);
|
||||||
if (!preg_match($patterns['FullPattern'], $filename)) {
|
if (!preg_match($patterns['FullPattern'], $filename)) {
|
||||||
@ -836,11 +836,11 @@ class Image extends File implements Flushable {
|
|||||||
}
|
}
|
||||||
// All generate functions may appear any number of times in the image cache name.
|
// All generate functions may appear any number of times in the image cache name.
|
||||||
$generateFuncs = implode('|', $generateFuncs);
|
$generateFuncs = implode('|', $generateFuncs);
|
||||||
$base64Match = "[a-zA-Z0-9\/\r\n+]*={0,2}";
|
$base64url_match = "[a-zA-Z0-9_~]*={0,2}";
|
||||||
return array(
|
return array(
|
||||||
'FullPattern' => "/^((?P<Generator>{$generateFuncs})(?P<Args>" . $base64Match . ")\-)+"
|
'FullPattern' => "/^((?P<Generator>{$generateFuncs})(?P<Args>" . $base64url_match . ")\-)+"
|
||||||
. preg_quote($filename) . "$/i",
|
. preg_quote($filename) . "$/i",
|
||||||
'GeneratorPattern' => "/(?P<Generator>{$generateFuncs})(?P<Args>" . $base64Match . ")\-/i"
|
'GeneratorPattern' => "/(?P<Generator>{$generateFuncs})(?P<Args>" . $base64url_match . ")\-/i"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -877,7 +877,7 @@ class Image extends File implements Flushable {
|
|||||||
$generatorArray = array();
|
$generatorArray = array();
|
||||||
foreach ($subMatches as $singleMatch) {
|
foreach ($subMatches as $singleMatch) {
|
||||||
$generatorArray[] = array('Generator' => $singleMatch['Generator'],
|
$generatorArray[] = array('Generator' => $singleMatch['Generator'],
|
||||||
'Args' => json_decode(base64_decode($singleMatch['Args'])));
|
'Args' => Convert::base64url_decode($singleMatch['Args']));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using array_reverse is important, as a cached image will
|
// Using array_reverse is important, as a cached image will
|
||||||
|
@ -36,6 +36,9 @@ class ConvertTest extends SapphireTest {
|
|||||||
'Normal text is not escaped');
|
'Normal text is not escaped');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::html2raw()}
|
||||||
|
*/
|
||||||
public function testHtml2raw() {
|
public function testHtml2raw() {
|
||||||
$val1 = 'This has a <strong>strong tag</strong>.';
|
$val1 = 'This has a <strong>strong tag</strong>.';
|
||||||
$this->assertEquals('This has a *strong tag*.', Convert::html2raw($val1),
|
$this->assertEquals('This has a *strong tag*.', Convert::html2raw($val1),
|
||||||
@ -139,6 +142,9 @@ PHP
|
|||||||
$this->assertEquals('This is some normal text.', Convert::xml2raw($val2), 'Normal text is not escaped');
|
$this->assertEquals('This is some normal text.', Convert::xml2raw($val2), 'Normal text is not escaped');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::xml2raw()}
|
||||||
|
*/
|
||||||
public function testArray2JSON() {
|
public function testArray2JSON() {
|
||||||
$val = array(
|
$val = array(
|
||||||
'Joe' => 'Bloggs',
|
'Joe' => 'Bloggs',
|
||||||
@ -152,6 +158,9 @@ PHP
|
|||||||
'Array is encoded in JSON');
|
'Array is encoded in JSON');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::json2array()}
|
||||||
|
*/
|
||||||
public function testJSON2Array() {
|
public function testJSON2Array() {
|
||||||
$val = '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}';
|
$val = '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}';
|
||||||
$decoded = Convert::json2array($val);
|
$decoded = Convert::json2array($val);
|
||||||
@ -161,6 +170,9 @@ PHP
|
|||||||
$this->assertContains('Structure', $decoded['My']['Complicated']);
|
$this->assertContains('Structure', $decoded['My']['Complicated']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::testJSON2Obj()}
|
||||||
|
*/
|
||||||
public function testJSON2Obj() {
|
public function testJSON2Obj() {
|
||||||
$val = '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}';
|
$val = '{"Joe":"Bloggs","Tom":"Jones","My":{"Complicated":"Structure"}}';
|
||||||
$obj = Convert::json2obj($val);
|
$obj = Convert::json2obj($val);
|
||||||
@ -170,6 +182,7 @@ PHP
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tests {@link Convert::testRaw2URL()}
|
||||||
* @todo test toASCII()
|
* @todo test toASCII()
|
||||||
*/
|
*/
|
||||||
public function testRaw2URL() {
|
public function testRaw2URL() {
|
||||||
@ -196,6 +209,9 @@ PHP
|
|||||||
$this->assertEquals($expected, $actual, $message);
|
$this->assertEquals($expected, $actual, $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::nl2os()}
|
||||||
|
*/
|
||||||
public function testNL2OS() {
|
public function testNL2OS() {
|
||||||
|
|
||||||
foreach(array("\r\n", "\r", "\n") as $nl) {
|
foreach(array("\r\n", "\r", "\n") as $nl) {
|
||||||
@ -229,6 +245,9 @@ PHP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::raw2js()}
|
||||||
|
*/
|
||||||
public function testRaw2JS() {
|
public function testRaw2JS() {
|
||||||
// Test attempt to break out of string
|
// Test attempt to break out of string
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
@ -255,6 +274,9 @@ PHP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::raw2json()}
|
||||||
|
*/
|
||||||
public function testRaw2JSON() {
|
public function testRaw2JSON() {
|
||||||
|
|
||||||
// Test object
|
// Test object
|
||||||
@ -281,6 +303,9 @@ PHP
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::xml2array()}
|
||||||
|
*/
|
||||||
public function testXML2Array() {
|
public function testXML2Array() {
|
||||||
// Ensure an XML file at risk of entity expansion can be avoided safely
|
// Ensure an XML file at risk of entity expansion can be avoided safely
|
||||||
$inputXML = <<<XML
|
$inputXML = <<<XML
|
||||||
@ -331,4 +356,44 @@ XML
|
|||||||
$result
|
$result
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests {@link Convert::base64url_encode()} and {@link Convert::base64url_decode()}
|
||||||
|
*/
|
||||||
|
public function testBase64url() {
|
||||||
|
$data = 'Wëīrð characters ☺ such as ¤Ø¶÷╬';
|
||||||
|
// This requires this test file to have UTF-8 character encoding
|
||||||
|
$this->assertEquals(
|
||||||
|
$data,
|
||||||
|
Convert::base64url_decode(Convert::base64url_encode($data))
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = 654.423;
|
||||||
|
$this->assertEquals(
|
||||||
|
$data,
|
||||||
|
Convert::base64url_decode(Convert::base64url_encode($data))
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = true;
|
||||||
|
$this->assertEquals(
|
||||||
|
$data,
|
||||||
|
Convert::base64url_decode(Convert::base64url_encode($data))
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = array('simple','array','¤Ø¶÷╬');
|
||||||
|
$this->assertEquals(
|
||||||
|
$data,
|
||||||
|
Convert::base64url_decode(Convert::base64url_encode($data))
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'a' => 'associative',
|
||||||
|
4 => 'array',
|
||||||
|
'☺' => '¤Ø¶÷╬'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
$data,
|
||||||
|
Convert::base64url_decode(Convert::base64url_encode($data))
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ class HtmlEditorFieldTest extends FunctionalTest {
|
|||||||
$this->assertEquals(10, (int)$xml[0]['width'], 'Width tag of resized image is set.');
|
$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(20, (int)$xml[0]['height'], 'Height tag of resized image is set.');
|
||||||
|
|
||||||
$neededFilename = 'assets/_resampled/ResizedImage' . base64_encode(json_encode(array(10,20))) .
|
$neededFilename = 'assets/_resampled/ResizedImage' . Convert::base64url_encode(array(10,20)) .
|
||||||
'-HTMLEditorFieldTest_example.jpg';
|
'-HTMLEditorFieldTest_example.jpg';
|
||||||
|
|
||||||
$this->assertEquals($neededFilename, (string)$xml[0]['src'], 'Correct URL of resized image is set.');
|
$this->assertEquals($neededFilename, (string)$xml[0]['src'], 'Correct URL of resized image is set.');
|
||||||
|
@ -287,7 +287,7 @@ class ImageTest extends SapphireTest {
|
|||||||
$imageFirst = $image->Pad(200,200,'CCCCCC');
|
$imageFirst = $image->Pad(200,200,'CCCCCC');
|
||||||
$imageFilename = $imageFirst->getFullPath();
|
$imageFilename = $imageFirst->getFullPath();
|
||||||
// Encoding of the arguments is duplicated from cacheFilename
|
// Encoding of the arguments is duplicated from cacheFilename
|
||||||
$neededPart = 'Pad' . base64_encode(json_encode(array(200,200,'CCCCCC')));
|
$neededPart = 'Pad' . Convert::base64url_encode(array(200,200,'CCCCCC'));
|
||||||
$this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated');
|
$this->assertContains($neededPart, $imageFilename, 'Filename for cached image is correctly generated');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,7 +310,7 @@ class ImageTest extends SapphireTest {
|
|||||||
|
|
||||||
$imageThird = $imageSecond->Pad(600,600,'0F0F0F');
|
$imageThird = $imageSecond->Pad(600,600,'0F0F0F');
|
||||||
// Encoding of the arguments is duplicated from cacheFilename
|
// Encoding of the arguments is duplicated from cacheFilename
|
||||||
$argumentString = base64_encode(json_encode(array(600,600,'0F0F0F')));
|
$argumentString = Convert::base64url_encode(array(600,600,'0F0F0F'));
|
||||||
$this->assertNotNull($imageThird);
|
$this->assertNotNull($imageThird);
|
||||||
$this->assertContains($argumentString, $imageThird->getFullPath(),
|
$this->assertContains($argumentString, $imageThird->getFullPath(),
|
||||||
'Image contains background color for padded resizement');
|
'Image contains background color for padded resizement');
|
||||||
@ -352,8 +352,8 @@ class ImageTest extends SapphireTest {
|
|||||||
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
|
$this->assertTrue(file_exists($p), 'Resized image exists after creation call');
|
||||||
|
|
||||||
// Encoding of the arguments is duplicated from cacheFilename
|
// Encoding of the arguments is duplicated from cacheFilename
|
||||||
$oldArgumentString = base64_encode(json_encode(array(200)));
|
$oldArgumentString = Convert::base64url_encode(array(200));
|
||||||
$newArgumentString = base64_encode(json_encode(array(300)));
|
$newArgumentString = Convert::base64url_encode(array(300));
|
||||||
|
|
||||||
$newPath = str_replace($oldArgumentString, $newArgumentString, $p);
|
$newPath = str_replace($oldArgumentString, $newArgumentString, $p);
|
||||||
$newRelative = str_replace($oldArgumentString, $newArgumentString, $image_generated->getFileName());
|
$newRelative = str_replace($oldArgumentString, $newArgumentString, $image_generated->getFileName());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user