ENHANCEMENT #3677 Added more rules for escaping reserved characters in XML to their proper entities e.g. ' => '

BUGFIX When exporting data from TableListField to a CSV via TableListField_Item->Fields(), don't convert the values to XML
MINOR Added test class ConvertTest with tests for various Convert functions



git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@77298 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Sean Harvey 2009-05-20 03:09:50 +00:00
parent 5e8b144318
commit e768d955df
3 changed files with 90 additions and 51 deletions

View File

@ -22,35 +22,47 @@
*/ */
class Convert extends Object { class Convert extends Object {
/**
* Convert a value to be suitable for an XML attribute.
*
* @param array|string $val String to escape, or array of strings
* @return array|string
*/
static function raw2att($val) { static function raw2att($val) {
if(is_array($val)) { if(is_array($val)) {
foreach($val as $k => $v) $val[$k] = self::raw2att($v); foreach($val as $k => $v) $val[$k] = self::raw2att($v);
return $val; return $val;
} else { } else {
return str_replace(array('&','"',"'",'<','>'),array('&amp;','&quot;','&#39;','&lt;','&gt;'),$val); return str_replace(array('&','"',"'",'<','>'), array('&amp;','&quot;','&apos;','&lt;','&gt;'), $val);
} }
} }
/** /**
* Convert a value to be suitable for an HTML attribute.
*
* This is useful for converting human readable values into
* a value suitable for an ID or NAME attribute.
*
* @see http://www.w3.org/TR/REC-html40/types.html#type-cdata * @see http://www.w3.org/TR/REC-html40/types.html#type-cdata
* @uses raw2att * @uses Convert::raw2att()
* @param array|string $val String to escape, or array of strings
* @return array|string
*/ */
static function raw2htmlatt($val) { static function raw2htmlatt($val) {
if(is_array($val)) { if(is_array($val)) {
foreach($val as $k => $v) $val[$k] = self::raw2att($v); foreach($val as $k => $v) $val[$k] = self::raw2htmlatt($v);
return $val; return $val;
} else { } else {
$val = str_replace(array('&','"',"'",'<','>'),array('&amp;','&quot;','&#39;','&lt;','&gt;'),$val); $val = self::raw2att($val);
$val = preg_replace('/[^a-zA-Z0-9\-_]*/','', $val); $val = preg_replace('/[^a-zA-Z0-9\-_]*/', '', $val);
return $val; return $val;
} }
} }
/** /**
* Ensure that text is properly escaped for XML. * Ensure that text is properly escaped for XML.
* *
* @see http://www.w3.org/TR/REC-xml/#dt-escape
* @param array|string $val String to escape, or array of strings * @param array|string $val String to escape, or array of strings
* @return array|string * @return array|string
*/ */
@ -59,7 +71,7 @@ class Convert extends Object {
foreach($val as $k => $v) $val[$k] = self::raw2xml($v); foreach($val as $k => $v) $val[$k] = self::raw2xml($v);
return $val; return $val;
} else { } else {
return str_replace(array('&', '<', '>', "\n"), array('&amp;', '&lt;', '&gt;', '<br />'), $val); return str_replace(array('&','<','>',"\n",'"',"'"), array('&amp;','&lt;','&gt;','<br />','&quot;','&apos;'), $val);
} }
} }
@ -79,7 +91,7 @@ class Convert extends Object {
} }
/** /**
* Uses the PHP5.2 native json_encode function if available, * Uses the PHP 5.2 native json_encode function if available,
* otherwise falls back to the Services_JSON class. * otherwise falls back to the Services_JSON class.
* *
* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198 * @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
@ -99,19 +111,17 @@ class Convert extends Object {
} }
} }
static function raw2sql($val) { static function raw2sql($val) {
if(is_array($val)) { if(is_array($val)) {
foreach($val as $k => $v) $val[$k] = self::raw2sql($v); foreach($val as $k => $v) $val[$k] = self::raw2sql($v);
return $val; return $val;
} else { } else {
return DB::getConn()->addslashes($val); return DB::getConn()->addslashes($val);
} }
} }
/** /**
* Convert XML to raw text * Convert XML to raw text.
* @uses html2raw() * @uses html2raw()
* @todo Currently &#xxx; entries are stripped; they should be converted * @todo Currently &#xxx; entries are stripped; they should be converted
*/ */
@ -119,18 +129,13 @@ class Convert extends Object {
if(is_array($val)) { if(is_array($val)) {
foreach($val as $k => $v) $val[$k] = self::xml2raw($v); foreach($val as $k => $v) $val[$k] = self::xml2raw($v);
return $val; return $val;
} else { } else {
// More complex text needs to use html2raw instead
// More complex text needs to use html2raw instaed
if(strpos($val,'<') !== false) return self::html2raw($val); if(strpos($val,'<') !== false) return self::html2raw($val);
// For simpler stuff, a simple str_replace will do $converted = str_replace(array('&amp;','&lt;','&gt;','&quot;','&apos;'), array('&','<','>','"',"'"), $val);
else { $converted = ereg_replace('&#[0-9]+;', '', $converted);
$converted = str_replace(array('&amp;', '&lt;', '&gt;', '&apos;'), array('&', '<', '>', "'"), $val); return $converted;
$converted = ereg_replace('&#[0-9]+;', '', $converted);
return $converted;
}
} }
} }
@ -191,7 +196,7 @@ class Convert extends Object {
} }
/** /**
* Uses the PHP5.2 native json_decode function if available, * Uses the PHP 5.2 native json_decode function if available,
* otherwise falls back to the Services_JSON class. * otherwise falls back to the Services_JSON class.
* *
* @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198 * @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198
@ -200,13 +205,9 @@ class Convert extends Object {
* @return mixed JSON safe string * @return mixed JSON safe string
*/ */
static function json2obj($val) { static function json2obj($val) {
//if(function_exists('json_decode')) { require_once(Director::baseFolder() . '/sapphire/thirdparty/json/JSON.php');
// return json_decode($val); $json = new Services_JSON();
//} else { return $json->decode($val);
require_once(Director::baseFolder() . '/sapphire/thirdparty/json/JSON.php');
$json = new Services_JSON();
return $json->decode($val);
//}
} }
/** /**
@ -264,8 +265,6 @@ class Convert extends Object {
return '{' . implode( ', ', $result ) . '}'; return '{' . implode( ', ', $result ) . '}';
} }
/** /**
* 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 The string to linkify
@ -278,18 +277,6 @@ class Convert extends Object {
return $string; return $string;
} }
/**
* Create a link if the string is a valid URL
* @param string The string to linkify
* @return A link to the URL if string is a URL
*/
/*static function mailtoIfMatch($string) {
if( preg_match( '/^[a-z+]+\:\/\/[a-zA-Z0-9$-_.+?&=!*\'()%]+$/', $string ) )
return "<a href=\"$string\">$string</a>";
else
return $string;
}*/
/** /**
* Simple conversion of HTML to plaintext. * Simple conversion of HTML to plaintext.
* *

View File

@ -957,7 +957,7 @@ JS
} }
$fieldItem = new TableListField_Item($item, $this); $fieldItem = new TableListField_Item($item, $this);
$fields = $fieldItem->Fields(); $fields = $fieldItem->Fields(false);
$columnData = array(); $columnData = array();
if($fields) foreach($fields as $field) { if($fields) foreach($fields as $field) {
$value = $field->Value; $value = $field->Value;
@ -1241,16 +1241,16 @@ class TableListField_Item extends ViewableData {
return $this->parent; return $this->parent;
} }
function Fields() { function Fields($xmlSafe = true) {
$list = $this->parent->FieldList(); $list = $this->parent->FieldList();
foreach($list as $fieldName => $fieldTitle) { foreach($list as $fieldName => $fieldTitle) {
$value = ""; $value = "";
// This supports simple FieldName syntax // This supports simple FieldName syntax
if(strpos($fieldName,'.') === false) { if(strpos($fieldName,'.') === false) {
$value = ($this->item->XML_val($fieldName)) ? $this->item->XML_val($fieldName) : $this->item->$fieldName; $value = ($this->item->XML_val($fieldName) && $xmlSafe) ? $this->item->XML_val($fieldName) : $this->item->$fieldName;
// This support the syntax fieldName = Relation.RelatedField } else {
} else { // This supports the syntax fieldName = Relation.RelatedField
$fieldNameParts = explode('.', $fieldName) ; $fieldNameParts = explode('.', $fieldName) ;
$tmpItem = $this->item; $tmpItem = $this->item;
for($j=0;$j<sizeof($fieldNameParts);$j++) { for($j=0;$j<sizeof($fieldNameParts);$j++) {
@ -1268,7 +1268,7 @@ class TableListField_Item extends ViewableData {
if(array_key_exists($fieldName, $this->parent->fieldCasting)) { if(array_key_exists($fieldName, $this->parent->fieldCasting)) {
$value = $this->parent->getCastedValue($value, $this->parent->fieldCasting[$fieldName]); $value = $this->parent->getCastedValue($value, $this->parent->fieldCasting[$fieldName]);
} }
// formatting // formatting
$item = $this->item; $item = $this->item;
if(array_key_exists($fieldName, $this->parent->fieldFormatting)) { if(array_key_exists($fieldName, $this->parent->fieldFormatting)) {
@ -1285,7 +1285,6 @@ class TableListField_Item extends ViewableData {
} }
} }
$fields[] = new ArrayData(array( $fields[] = new ArrayData(array(
"Name" => $fieldName, "Name" => $fieldName,
"Title" => $fieldTitle, "Title" => $fieldTitle,

53
tests/ConvertTest.php Normal file
View File

@ -0,0 +1,53 @@
<?php
/**
* Test various functions on the {@link Convert} class.
* @package sapphire
* @subpackage tests
*/
class ConvertTest extends SapphireTest {
/**
* Tests {@link Convert::raw2att()}
*/
function testRaw2Att() {
$val1 = '<input type="text">';
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2att($val1), 'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('This is some normal text.', Convert::raw2att($val2), 'Normal text is not escaped');
}
/**
* Tests {@link Convert::raw2htmlatt()}
*/
function testRaw2HtmlAtt() {
$val1 = '<input type="text">';
$this->assertEquals('ltinputtypequottextquotgt', Convert::raw2htmlatt($val1), 'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('Thisissomenormaltext', Convert::raw2htmlatt($val2), 'Normal text is not escaped');
}
/**
* Tests {@link Convert::raw2xml()}
*/
function testRaw2Xml() {
$val1 = '<input type="text">';
$this->assertEquals('&lt;input type=&quot;text&quot;&gt;', Convert::raw2xml($val1), 'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('This is some normal text.', Convert::raw2xml($val2), 'Normal text is not escaped');
}
/**
* Tests {@link Convert::xml2raw()}
*/
function testXml2Raw() {
$val1 = '&lt;input type=&quot;text&quot;&gt;';
$this->assertEquals('<input type="text">', Convert::xml2raw($val1), 'Special characters are escaped');
$val2 = 'This is some normal text.';
$this->assertEquals('This is some normal text.', Convert::xml2raw($val2), 'Normal text is not escaped');
}
}