API Add FormField::canSubmitValue()

API Add HTMLText::getProcessShortcodes() / setProcessShortcodes()
API Split TextareaField::Value() into ValueEntities() with shortcodes disabled
This commit is contained in:
Damian Mooyman 2016-11-15 13:34:54 +13:00
parent 8e5f786b8d
commit f43a91a4f8
8 changed files with 96 additions and 44 deletions

View File

@ -47,7 +47,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
protected $originalMailer; protected $originalMailer;
protected $originalMemberPasswordValidator; protected $originalMemberPasswordValidator;
protected $originalRequirements; protected $originalRequirements;
protected $originalIsRunningTest; protected $originalIsRunningTest;
@ -191,9 +191,9 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
self::$is_running_test = true; self::$is_running_test = true;
// i18n needs to be set to the defaults or tests fail // i18n needs to be set to the defaults or tests fail
i18n::set_locale(i18n::default_locale()); i18n::set_locale(i18n::config()->default_locale);
i18n::config()->date_format = null; i18n::config()->date_format = 'yyyy-MM-dd';
i18n::config()->time_format = null; i18n::config()->time_format = 'H:mm';
// Set default timezone consistently to avoid NZ-specific dependencies // Set default timezone consistently to avoid NZ-specific dependencies
date_default_timezone_set('UTC'); date_default_timezone_set('UTC');
@ -217,7 +217,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
Config::inst()->update('Director', 'rules', array( Config::inst()->update('Director', 'rules', array(
'$Controller//$Action/$ID/$OtherID' => '*' '$Controller//$Action/$ID/$OtherID' => '*'
)); ));
$fixtureFile = static::get_fixture_file(); $fixtureFile = static::get_fixture_file();
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
@ -367,7 +367,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
} }
} }
} }
//unnest injector / config now that the test suite is over //unnest injector / config now that the test suite is over
// this will reset all the extensions on the object too (see setUpOnce) // this will reset all the extensions on the object too (see setUpOnce)
Injector::unnest(); Injector::unnest();
@ -719,22 +719,22 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
. var_export($match, true) . ": " . var_export($item, true) . var_export($match, true) . ": " . var_export($item, true)
); );
} }
} }
/** /**
* Removes sequences of repeated whitespace characters from SQL queries * Removes sequences of repeated whitespace characters from SQL queries
* making them suitable for string comparison * making them suitable for string comparison
* *
* @param string $sql * @param string $sql
* @return string The cleaned and normalised SQL string * @return string The cleaned and normalised SQL string
*/ */
protected function normaliseSQL($sql) { protected function normaliseSQL($sql) {
return trim(preg_replace('/\s+/m', ' ', $sql)); return trim(preg_replace('/\s+/m', ' ', $sql));
} }
/** /**
* Asserts that two SQL queries are equivalent * Asserts that two SQL queries are equivalent
* *
* @param string $expectedSQL * @param string $expectedSQL
* @param string $actualSQL * @param string $actualSQL
* @param string $message * @param string $message
@ -749,7 +749,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
// Normalise SQL queries to remove patterns of repeating whitespace // Normalise SQL queries to remove patterns of repeating whitespace
$expectedSQL = $this->normaliseSQL($expectedSQL); $expectedSQL = $this->normaliseSQL($expectedSQL);
$actualSQL = $this->normaliseSQL($actualSQL); $actualSQL = $this->normaliseSQL($actualSQL);
$this->assertEquals($expectedSQL, $actualSQL, $message, $delta, $maxDepth, $canonicalize, $ignoreCase); $this->assertEquals($expectedSQL, $actualSQL, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
} }
@ -813,7 +813,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
public static function using_temp_db() { public static function using_temp_db() {
$dbConn = DB::get_conn(); $dbConn = DB::get_conn();
$prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_'; $prefix = defined('SS_DATABASE_PREFIX') ? SS_DATABASE_PREFIX : 'ss_';
return $dbConn && (substr($dbConn->getSelectedDatabase(), 0, strlen($prefix) + 5) return $dbConn && (substr($dbConn->getSelectedDatabase(), 0, strlen($prefix) + 5)
== strtolower(sprintf('%stmpdb', $prefix))); == strtolower(sprintf('%stmpdb', $prefix)));
} }
@ -919,7 +919,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
if(!($SNG instanceof TestOnly)) $SNG->requireTable(); if(!($SNG instanceof TestOnly)) $SNG->requireTable();
} }
} }
// If we have additional dataobjects which need schema, do so here: // If we have additional dataobjects which need schema, do so here:
if($extraDataObjects) { if($extraDataObjects) {
foreach($extraDataObjects as $dataClass) { foreach($extraDataObjects as $dataClass) {
@ -997,7 +997,7 @@ class SapphireTest extends PHPUnit_Framework_TestCase {
// Remove all the test themes we created // Remove all the test themes we created
SS_TemplateLoader::instance()->popManifest(); SS_TemplateLoader::instance()->popManifest();
Config::unnest(); Config::unnest();
if ($e) throw $e; if ($e) throw $e;

View File

@ -47,14 +47,23 @@ class FieldList extends ArrayList {
/** /**
* Return a sequential set of all fields that have data. This excludes wrapper composite fields * Return a sequential set of all fields that have data. This excludes wrapper composite fields
* as well as heading / help text fields. * as well as heading / help text fields.
*
* @return array
*/ */
public function dataFields() { public function dataFields() {
if(!$this->sequentialSet) $this->collateDataFields($this->sequentialSet); if(!$this->sequentialSet) {
$this->collateDataFields($this->sequentialSet);
}
return $this->sequentialSet; return $this->sequentialSet;
} }
/**
* @return array
*/
public function saveableFields() { public function saveableFields() {
if(!$this->sequentialSaveableSet) $this->collateDataFields($this->sequentialSaveableSet, true); if(!$this->sequentialSaveableSet) {
$this->collateDataFields($this->sequentialSaveableSet, true);
}
return $this->sequentialSaveableSet; return $this->sequentialSaveableSet;
} }
@ -64,13 +73,20 @@ class FieldList extends ArrayList {
} }
protected function collateDataFields(&$list, $saveableOnly = false) { protected function collateDataFields(&$list, $saveableOnly = false) {
// Initialise list
if (!isset($list)) {
$list = array();
}
/** @var FormField $field */
foreach($this as $field) { foreach($this as $field) {
if($field->isComposite()) $field->collateDataFields($list, $saveableOnly); if($field->isComposite()) {
$field->collateDataFields($list, $saveableOnly);
}
if($saveableOnly) { if($saveableOnly) {
$isIncluded = ($field->hasData() && !$field->isReadonly() && !$field->isDisabled()); $isIncluded = $field->canSubmitValue();
} else { } else {
$isIncluded = ($field->hasData()); $isIncluded = $field->hasData();
} }
if($isIncluded) { if($isIncluded) {
$name = $field->getName(); $name = $field->getName();

View File

@ -355,18 +355,8 @@ class Form extends RequestHandler {
$vars = $request->requestVars(); $vars = $request->requestVars();
} }
// construct an array of allowed fields that can be populated from request data. // Ensure we only process saveable fields (non structural, readonly, or disabled)
// readonly or disabled fields should not be loading data from requests $allowedFields = array_keys($this->Fields()->saveableFields());
$allowedFields = array();
$dataFields = $this->Fields()->dataFields();
if ($dataFields) {
/** @var FormField $field */
foreach ($this->Fields()->dataFields() as $name => $field) {
if (!$field->isReadonly() && !$field->isDisabled()) {
$allowedFields[] = $name;
}
}
}
// Populate the form // Populate the form
$this->loadDataFrom($vars, true, $allowedFields); $this->loadDataFrom($vars, true, $allowedFields);
@ -1392,7 +1382,7 @@ class Form extends RequestHandler {
* For backwards compatibility reasons, this parameter can also be set to === true, which is the same as passing * For backwards compatibility reasons, this parameter can also be set to === true, which is the same as passing
* CLEAR_MISSING * CLEAR_MISSING
* *
* @param FieldList $fieldList An optional list of fields to process. This can be useful when you have a * @param array $fieldList An optional list of fields to process. This can be useful when you have a
* form that has some fields that save to one object, and some that save to another. * form that has some fields that save to one object, and some that save to another.
* @return Form * @return Form
*/ */
@ -1669,7 +1659,7 @@ class Form extends RequestHandler {
/** /**
* Get a list of all actions, including those in the main "fields" FieldList * Get a list of all actions, including those in the main "fields" FieldList
* *
* @return array * @return array
*/ */
protected function getAllActions() { protected function getAllActions() {

View File

@ -1277,4 +1277,13 @@ class FormField extends RequestHandler {
return $field; return $field;
} }
/**
* Determine if the value of this formfield accepts front-end submitted values and is saveable.
*
* @return bool
*/
public function canSubmitValue() {
return $this->hasData() && !$this->isReadonly() && !$this->isDisabled();
}
} }

View File

@ -26,14 +26,6 @@ class HtmlEditorField extends TextareaField {
*/ */
private static $sanitise_server_side = false; private static $sanitise_server_side = false;
/**
* @config
* @var array
*/
private static $casting = array(
'Value' => 'HTMLText',
);
protected $rows = 30; protected $rows = 30;
/** /**

View File

@ -19,8 +19,13 @@
*/ */
class TextareaField extends FormField { class TextareaField extends FormField {
/**
* Value should be XML
*
* @var array
*/
private static $casting = array( private static $casting = array(
'Value' => 'HTMLText', 'Value' => 'HTMLText(array(\'shortcodes\' => false))',
); );
/** /**
@ -93,7 +98,9 @@ class TextareaField extends FormField {
} }
/** /**
* @return string * Return value with all values encoded in html entities
*
* @return string Raw HTML
*/ */
public function Value() { public function Value() {
return htmlentities($this->value, ENT_COMPAT, 'UTF-8'); return htmlentities($this->value, ENT_COMPAT, 'UTF-8');

View File

@ -34,6 +34,26 @@ class HTMLText extends Text {
protected $processShortcodes = true; protected $processShortcodes = true;
/**
* Check if shortcodes are enabled
*
* @return bool
*/
public function getProcessShortcodes() {
return $this->processShortcodes;
}
/**
* Set shortcodes on or off by default
*
* @param bool $process
* @return $this
*/
public function setProcessShortcodes($process) {
$this->processShortcodes = (bool)$process;
return $this;
}
protected $whitelist = false; protected $whitelist = false;
public function __construct($name = null, $options = array()) { public function __construct($name = null, $options = array()) {

View File

@ -16,7 +16,7 @@ class TextareaFieldTest extends SapphireTest {
/** /**
* Quick smoke test to ensure that text with special html chars is being displayed properly in readonly fields. * Quick smoke test to ensure that text with special html chars is being displayed properly in readonly fields.
*/ */
public function testReadonlyDisplaySepcialHTML() { public function testReadonlyDisplaySpecialHTML() {
$inputText = "These are some special <html> chars including 'single' & \"double\" quotations"; $inputText = "These are some special <html> chars including 'single' & \"double\" quotations";
$field = new TextareaField("Test", "Test"); $field = new TextareaField("Test", "Test");
$field = $field->performReadonlyTransformation(); $field = $field->performReadonlyTransformation();
@ -25,4 +25,22 @@ class TextareaFieldTest extends SapphireTest {
. ' &quot;double&quot; quotations', $field->Field()); . ' &quot;double&quot; quotations', $field->Field());
} }
public function testValueEntities() {
$inputText = "These <b>are</b> some unicodes: äöü";
$field = new TextareaField("Test", "Test");
$field->setValue($inputText);
// Value should be safe-encoding only, but ValueEntities should be more aggressive
$this->assertEquals(
"These &lt;b&gt;are&lt;/b&gt; some unicodes: &auml;&ouml;&uuml;",
$field->obj('Value')->forTemplate()
);
// Shortcodes are disabled
$this->assertEquals(
false,
$field->obj('Value')->getProcessShortcodes()
);
}
} }