validationError(
$this->name,
- _t('DateField.VALIDDATEFORMAT', "Please enter a valid date format (DD/MM/YYYY)."),
+ _t('DateField.VALIDDATEFORMAT', "Please enter a valid date format (DD/MM/YYYY)."),
"validation",
false
);
@@ -130,5 +129,9 @@ class DateField_Disabled extends DateField {
function php() {
return true;
}
+
+ function validate($validator) {
+ return true;
+ }
}
?>
\ No newline at end of file
diff --git a/forms/TableField.php b/forms/TableField.php
index 3b6779814..b7c1c0496 100644
--- a/forms/TableField.php
+++ b/forms/TableField.php
@@ -370,7 +370,7 @@ class TableField extends TableListField {
if($dataObjects) {
foreach ($dataObjects as $objectid => $fieldValues) {
// we have to "sort" new data first, and process it in a seperate saveData-call (see setValue())
- if($objectid === "new") continue;
+ if($objectid === "new") continue;
// extra data was creating fields, but
if($this->extraData) {
@@ -381,7 +381,7 @@ class TableField extends TableListField {
$obj = new $this->sourceClass();
if($ExistingValues) {
- $obj->ID = $objectid;
+ $obj = DataObject::get_by_id($this->sourceClass, $objectid);
}
// Legacy: Use the filter as a predefined relationship-ID
@@ -553,7 +553,10 @@ JS;
if($data['methodName'] != 'delete'){
$fields = $this->FieldSet();
$fields = new FieldSet($fields);
-
+ foreach($fields as $field){
+ $valid = $field->validate($this) && $valid;
+ }
+ return $valid;
}else{
return $valid;
}
diff --git a/forms/TableListField.php b/forms/TableListField.php
index 816db38fc..8d14917b1 100755
--- a/forms/TableListField.php
+++ b/forms/TableListField.php
@@ -211,6 +211,8 @@ class TableListField extends FormField {
*/
public $fieldFormatting = array();
+ public $csvFieldFormatting = array();
+
/**
* @var string
*/
@@ -227,6 +229,8 @@ class TableListField extends FormField {
*/
protected $extraLinkParams;
+ protected $__cachedQuery;
+
function __construct($name, $sourceClass, $fieldList = null, $sourceFilter = null,
$sourceSort = null, $sourceJoin = null) {
@@ -259,6 +263,19 @@ class TableListField extends FormField {
return $this->FieldHolder();
}
+ static $url_handlers = array(
+ 'item/$ID' => 'handleItem',
+ '$Action!' => '$Action',
+ );
+
+ function sourceClass() {
+ return $this->sourceClass;
+ }
+
+ function handleItem($request) {
+ return new TableListField_ItemRequest($this, $request->param('ID'));
+ }
+
function FieldHolder() {
if($this->clickAction) {
@@ -321,7 +338,7 @@ JS
return false;
}
- if($this->__cachedSQL) {
+ if($this->__cachedQuery) {
$query = $this->__cachedQuery;
} else {
$query = $this->__cachedQuery = $this->getQuery();
@@ -423,7 +440,7 @@ JS
*/
function getQuery() {
if($this->customQuery) {
- $query = $this->customQuery;
+ $query = clone $this->customQuery;
$baseClass = ClassInfo::baseDataClass($this->sourceClass);
$query->select[] = "{$baseClass}.ID AS ID";
$query->select[] = "{$baseClass}.ClassName AS ClassName";
@@ -448,7 +465,7 @@ JS
$query->orderby = $SQL_sort;
}
}
- return clone $query;
+ return $query;
}
function getCsvQuery() {
@@ -605,6 +622,8 @@ JS
$summaryFields[] = new ArrayData(array(
'Function' => $function,
'SummaryValue' => $summaryValue,
+ 'Name' => DBField::create('Varchar', $fieldName),
+ 'Title' => DBField::create('Varchar', $fieldTitle),
));
}
return new DataObjectSet($summaryFields);
@@ -879,6 +898,7 @@ JS
function export() {
$now = Date("d-m-Y-H-i");
$fileName = "export-$now.csv";
+
$separator = $this->csvSeparator;
$csvColumns = ($this->fieldListCsv) ? $this->fieldListCsv : $this->fieldList;
$fileData = "";
@@ -889,14 +909,15 @@ JS
}
// get data
- $dataQuery = $this->getCsvQuery();
- $records = $dataQuery->execute();
-
- $sourceClass = $this->sourceClass;
- $dataobject = new $sourceClass();
-
- // @todo Will create a large unpaginated dataobjectset based on how many records are in table (performance issue)
- $items = $dataobject->buildDataObjectSet($records, 'DataObjectSet');
+ if(isset($this->customSourceItems)){
+ $items = $this->customSourceItems;
+ }else{
+ $dataQuery = $this->getCsvQuery();
+ $records = $dataQuery->execute();
+ $sourceClass = $this->sourceClass;
+ $dataobject = new $sourceClass();
+ $items = $dataobject->buildDataObjectSet($records, 'DataObjectSet');
+ }
$fieldItems = new DataObjectSet();
if($items && $items->count()) foreach($items as $item) {
@@ -911,26 +932,31 @@ JS
if($fieldItems) {
foreach($fieldItems as $fieldItem) {
+ $columnData = array();
$fields = $fieldItem->Fields();
foreach($fields as $field) {
- // replace
s with newlines for csv
- $field->Value = str_replace('
', "\n", $field->Value);
- // remove double quotes
- $field->Value = str_replace('"', "", $field->Value);
- $fileData .= "\"" . $field->Value . "\"";
- if($field->Last()) {
- $fileData .= "\n";
- } else {
- $fileData .= $this->csvSeparator;
+
+ $value = $field->Value;
+
+ // TODO This should be replaced with casting
+ if(array_key_exists($field->Name, $this->csvFieldFormatting)) {
+ $format = str_replace('$value', "__VAL__", $this->csvFieldFormatting[$columnName]);
+ $format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format);
+ $format = str_replace('__VAL__', '$value', $format);
+ eval('$value = "' . $format . '";');
}
+
+ $value = str_replace(array("\r", "\n"), "\n", $value);
+ $tmpColumnData = "\"" . str_replace("\"", "\"\"", $value) . "\"";
+ $columnData[] = $tmpColumnData;
}
-
+ $fileData .= implode($separator, $columnData);
+ $fileData .= "\n";
}
return HTTPRequest::send_file($fileData, $fileName);
} else {
user_error("No records found", E_USER_ERROR);
}
-
}
/**
@@ -948,12 +974,20 @@ JS
Requirements::css(CMS_DIR . '/css/typography.css');
Requirements::css(CMS_DIR . '/css/cms_right.css');
Requirements::css(SAPPHIRE_DIR . '/css/TableListField_print.css');
- $vd = new ViewableData();
- return $vd->customise(array(
- 'Content' => $this->customise(array(
- 'Print' => true
- ))->renderWith($this->template)
- ))->renderWith('TableListField_printable');
+
+ unset($this->cachedSourceItems);
+ $oldShowPagination = $this->showPagination;
+ $this->showPagination = false;
+ $oldLimit = ini_get('max_execution_time');
+ set_time_limit(0);
+
+
+ $result = $this->renderWith(array($this->template . '_printable', 'TableListField_printable'));
+
+ $this->showPagination = $oldShowPagination;
+ set_time_limit($oldLimit);
+
+ return $result;
}
function PrintLink() {
@@ -1010,6 +1044,10 @@ JS
$this->fieldFormatting = $formatting;
}
+ function setCSVFieldFormatting($formatting) {
+ $this->csvFieldFormatting = $formatting;
+ }
+
/**
* @return String
*/
@@ -1024,19 +1062,19 @@ JS
// adding this to TODO probably add a method to the classes
// to return they're translated string
// added by ruibarreiros @ 27/11/2007
- return singleton($this->sourceClass)->singular_name();
+ return $this->sourceClass ? singleton($this->sourceClass)->singular_name() : $this->Name();
}
function NameSingular() {
// same as Title()
// added by ruibarreiros @ 27/11/2007
- return singleton($this->sourceClass)->singular_name();
+ return $this->sourceClass ? singleton($this->sourceClass)->singular_name() : $this->Name();
}
function NamePlural() {
// same as Title()
// added by ruibarreiros @ 27/11/2007
- return singleton($this->sourceClass)->plural_name();
+ return $this->sourceClass ? singleton($this->sourceClass)->plural_name() : $this->Name();
}
function setTemplate($template) {
@@ -1216,6 +1254,16 @@ class TableListField_Item extends ViewableData {
return $this->parent->Can($mode);
}
+ function Link() {
+ if($this->parent->getForm()) {
+ return Controller::join_links($this->parent->Link() . "item/" . $this->item->ID);
+ } else {
+ // allow for instanciation of this FormField outside of a controller/form
+ // context (e.g. for unit tests)
+ return false;
+ }
+ }
+
/**
* Returns all row-based actions not disallowed through permissions.
* See TableListField->Action for a similiar dummy-function to work
@@ -1240,17 +1288,7 @@ class TableListField_Item extends ViewableData {
return $allowedActions;
}
-
- function Link() {
- if($this->parent->getForm()) {
- return Controller::join_links($this->parent->Link() . "item/" . $this->item->ID);
- } else {
- // allow for instanciation of this FormField outside of a controller/form
- // context (e.g. for unit tests)
- return false;
- }
- }
-
+
function BaseLink() {
user_error("TableListField_Item::BaseLink() deprecated, use Link() instead", E_USER_NOTICE);
return $this->Link();
@@ -1293,7 +1331,78 @@ class TableListField_Item extends ViewableData {
function isReadonly() {
return $this->parent->Can('delete');
}
-
}
-?>
\ No newline at end of file
+class TableListField_ItemRequest extends RequestHandlingData {
+ protected $ctf;
+ protected $itemID;
+ protected $methodName;
+
+ static $url_handlers = array(
+ '$Action!' => '$Action',
+ '' => 'index',
+ );
+
+ function Link() {
+ return $this->ctf->Link() . '/item/' . $this->itemID;
+ }
+
+ function __construct($ctf, $itemID) {
+ $this->ctf = $ctf;
+ $this->itemID = $itemID;
+ }
+
+ function delete() {
+ if($this->ctf->Can('delete') !== true) {
+ return false;
+ }
+
+ $this->dataObj()->delete();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Return the data object being manipulated
+ */
+ function dataObj() {
+ // used to discover fields if requested and for population of field
+ if(is_numeric($this->itemID)) {
+ // we have to use the basedataclass, otherwise we might exclude other subclasses
+ return DataObject::get_by_id(ClassInfo::baseDataClass(Object::getCustomClass($this->ctf->sourceClass())), $this->itemID);
+ }
+
+ }
+
+ /**
+ * Returns the db-fieldname of the currently used has_one-relationship.
+ */
+ function getParentIdName( $parentClass, $childClass ) {
+ return $this->getParentIdNameRelation( $childClass, $parentClass, 'has_one' );
+ }
+
+ /**
+ * Manually overwrites the parent-ID relations.
+ * @see setParentClass()
+ *
+ * @param String $str Example: FamilyID (when one Individual has_one Family)
+ */
+ function setParentIdName($str) {
+ $this->parentIdName = $str;
+ }
+
+ /**
+ * Returns the db-fieldname of the currently used relationship.
+ */
+ function getParentIdNameRelation($parentClass, $childClass, $relation) {
+ if($this->parentIdName) return $this->parentIdName;
+
+ $relations = singleton($parentClass)->$relation();
+ $classes = ClassInfo::ancestry($childClass);
+ foreach($relations as $k => $v) {
+ if(array_key_exists($v, $classes)) return $k . 'ID';
+ }
+ return false;
+ }
+}
+?>
diff --git a/javascript/ComplexTableField.js b/javascript/ComplexTableField.js
index 03ad901f7..ea71ad12b 100755
--- a/javascript/ComplexTableField.js
+++ b/javascript/ComplexTableField.js
@@ -4,13 +4,16 @@ GB_RefreshLink = "";
ComplexTableField = Class.create();
ComplexTableField.prototype = {
- // TODO adjust dynamically
- popupWidth: 560,
- popupHeight: 390,
+ // These are defaults used if setPopupSize encounters errors
+ defaultPopupWidth: 560,
+ defaultPopupHeight: 390,
initialize: function() {
var rules = {};
rules['#'+this.id+' table.data a.popuplink'] = {onclick: this.openPopup.bind(this)};
+
+ // Assume that the delete link uses the deleteRecord method
+ rules['#'+this.id+' table.data a.deletelink'] = {onclick: this.deleteRecord.bind(this)};
rules['#'+this.id+' table.data tbody td'] = {onclick: this.openPopup.bind(this)};
// invoke row action-link based on default-action set in classname
@@ -25,10 +28,22 @@ ComplexTableField.prototype = {
}
Behaviour.register('ComplexTableField_'+this.id,rules);
+ this.setPopupSize();
+
// HACK If already in a popup, we can't allow add (doesn't save existing relation correctly)
if(window != top) $$('#'+this.id+' table.data a.addlink').each(function(el) {Element.hide(el);});
},
+ setPopupSize: function() {
+ try {
+ this.popupHeight = parseInt($(this.id + '_PopupHeight').value);
+ this.popupWidth = parseInt($(this.id + '_PopupWidth').value);
+ } catch (ex) {
+ this.popupHeight = this.defaultPopupHeight;
+ this.popupWidth = this.defaultPopupWidth;
+ }
+ },
+
getDefaultAction: function() {
// try to get link class from 0) return labels[0].innerHTML;
+ }
+
return findParentLabel(el.parentNode);
}
}
diff --git a/security/Authenticator.php b/security/Authenticator.php
index 9a1c4eb0a..e61ca898b 100644
--- a/security/Authenticator.php
+++ b/security/Authenticator.php
@@ -16,7 +16,7 @@ abstract class Authenticator extends Object {
*
* @var array
*/
- private static $authenticators = array();
+ private static $authenticators = array('MemberAuthenticator');
/**
* Used to influence the order of authenticators on the login-screen
@@ -24,7 +24,7 @@ abstract class Authenticator extends Object {
*
* @var string
*/
- private static $default_authenticator = '';
+ private static $default_authenticator = 'MemberAuthenticator';
/**
@@ -107,7 +107,9 @@ abstract class Authenticator extends Object {
*/
public static function unregister_authenticator($authenticator) {
if(call_user_func(array($authenticator, 'on_unregister')) === true) {
- unset(self::$authenticators[$authenticator]);
+ if(in_array($authenticator, self::$authenticators)) {
+ unset(self::$authenticators[array_search($authenticator, self::$authenticators)]);
+ }
};
}
diff --git a/security/LoginForm.php b/security/LoginForm.php
index 21b2a2b1e..92537bf36 100644
--- a/security/LoginForm.php
+++ b/security/LoginForm.php
@@ -33,7 +33,7 @@ abstract class LoginForm extends Form {
public function getAuthenticator() {
if(!class_exists($this->authenticator_class) || !is_subclass_of($this->authenticator_class, 'Authenticator')) {
- user_error('The form uses an invalid authenticator class!', E_USER_ERROR);
+ user_error("The form uses an invalid authenticator class! '{$this->authenticator_class}' is not a subclass of 'Authenticator'", E_USER_ERROR);
return;
}
diff --git a/security/MemberLoginForm.php b/security/MemberLoginForm.php
index 54f618a65..05f38d65f 100644
--- a/security/MemberLoginForm.php
+++ b/security/MemberLoginForm.php
@@ -6,6 +6,8 @@
*/
class MemberLoginForm extends LoginForm {
+ protected $authenticator_class = 'MemberAuthenticator';
+
/**
* Constructor
*
@@ -22,11 +24,13 @@ class MemberLoginForm extends LoginForm {
* @param bool $checkCurrentUser If set to TRUE, it will be checked if a
* the user is currently logged in, and if
* so, only a logout button will be rendered
+ * @param string $authenticatorClassName Name of the authenticator class that this form uses.
*/
function __construct($controller, $name, $fields = null, $actions = null,
$checkCurrentUser = true) {
- $this->authenticator_class = 'MemberAuthenticator';
+ // This is now set on the class directly to make it easier to create subclasses
+ // $this->authenticator_class = $authenticatorClassName;
$customCSS = project() . '/css/member_login.css';
if(Director::fileExists($customCSS)) {
@@ -48,10 +52,16 @@ class MemberLoginForm extends LoginForm {
new HiddenField("AuthenticationMethod", null, $this->authenticator_class, $this),
new TextField("Email", _t('Member.EMAIL'),
Session::get('SessionForms.MemberLoginForm.Email'), null, $this),
- new EncryptField("Password", _t('Member.PASSWORD'), null, $this),
- new CheckboxField("Remember", _t('Member.REMEMBERME', "Remember me next time?"),
- Session::get('SessionForms.MemberLoginForm.Remember'), $this)
+ new EncryptField("Password", _t('Member.PASSWORD'), null, $this)
);
+ if(Security::$autologin_enabled) {
+ $fields->push(new CheckboxField(
+ "Remember",
+ _t('Member.REMEMBERME', "Remember me next time?"),
+ Session::get('SessionForms.MemberLoginForm.Remember'),
+ $this
+ ));
+ }
}
if(!$actions) {
$actions = new FieldSet(
@@ -109,7 +119,7 @@ class MemberLoginForm extends LoginForm {
Session::clear("BackURL");
Director::redirect($backURL);
} else {
- Director::redirect(Security::default_login_dest());
+ Director::redirectBack();
}
} else {
Session::set('SessionForms.MemberLoginForm.Email', $data['Email']);
@@ -187,7 +197,7 @@ class MemberLoginForm extends LoginForm {
$member->sendInfo('forgotPassword', array('PasswordResetLink' =>
Security::getPasswordResetLink($member->AutoLoginHash)));
- Director::redirect('Security/passwordsent/?email=' . urlencode($data['Email']));
+ Director::redirect('Security/passwordsent/' . urlencode($data['Email']));
} else if($data['Email']) {
$this->sessionMessage(
diff --git a/security/Security.php b/security/Security.php
index 804df3893..19bf7d341 100644
--- a/security/Security.php
+++ b/security/Security.php
@@ -52,6 +52,14 @@ class Security extends Controller {
*/
protected static $useSalt = true;
+ /**
+ * Showing "Remember me"-checkbox
+ * on loginform, and saving encrypted credentials to a cookie.
+ *
+ * @var bool
+ */
+ public static $autologin_enabled = true;
+
/**
* Location of word list to use for generating passwords
*
@@ -207,8 +215,7 @@ class Security extends Controller {
$authenticators = Authenticator::get_authenticators();
if(in_array($authenticator, $authenticators)) {
- return call_user_func(array($authenticator, 'get_login_form'),
- $this);
+ return call_user_func(array($authenticator, 'get_login_form'), $this);
}
}
diff --git a/templates/Includes/TableListField_Summary.ss b/templates/Includes/TableListField_Summary.ss
index 5480c03bf..f42a6820b 100755
--- a/templates/Includes/TableListField_Summary.ss
+++ b/templates/Includes/TableListField_Summary.ss
@@ -1,6 +1,6 @@
<% if Markable %> | | <% end_if %>
$SummaryTitle |
<% control SummaryFields %>
- class="$Function"<% end_if %>>$SummaryValue |
+ $SummaryValue |
<% end_control %>
<% if Can(delete) %> | <% end_if %>
\ No newline at end of file
diff --git a/templates/TableListField.ss b/templates/TableListField.ss
index 91d6ca07b..9e792bdba 100755
--- a/templates/TableListField.ss
+++ b/templates/TableListField.ss
@@ -1,9 +1,9 @@
-
+
<% if Print %><% else %><% include TableListField_PageControls %><% end_if %>
- <% if Markable %> | <% end_if %>
+ <% if Markable %><% if MarkableTitle %>$MarkableTitle<% else %> <% end_if %> | <% end_if %>
<% if Print %>
<% control Headings %>
diff --git a/tests/DataObjectTest.php b/tests/DataObjectTest.php
index 6f82feae0..deef7753b 100644
--- a/tests/DataObjectTest.php
+++ b/tests/DataObjectTest.php
@@ -241,6 +241,20 @@ class DataObjectTest extends SapphireTest {
),
'Changed fields are correctly detected while ignoring type changes (level=2)'
);
+
+ $newPage = new Page();
+ $newPage->Title = "New Page Title";
+ $this->assertEquals(
+ $newPage->getChangedFields(false, 2),
+ array(
+ 'Title' => array(
+ 'before' => null,
+ 'after' => 'New Page Title',
+ 'level' => 2
+ )
+ ),
+ 'Initialised fields are correctly detected as full changes'
+ );
}
function testRandomSort() {
diff --git a/tests/forms/TableListFieldTest.php b/tests/forms/TableListFieldTest.php
index 20469e149..47e512c55 100644
--- a/tests/forms/TableListFieldTest.php
+++ b/tests/forms/TableListFieldTest.php
@@ -11,6 +11,11 @@ class TableListFieldTest extends SapphireTest {
"D" => "Col D",
"E" => "Col E",
));
+ // A TableListField must be inside a form for its links to be generated
+ $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
+ $table
+ ), new FieldSet());
+
$result = $table->FieldHolder();
// Do a quick check to ensure that some of the D() and getE() values got through
@@ -28,6 +33,11 @@ class TableListFieldTest extends SapphireTest {
"D" => "Col D",
"E" => "Col E",
));
+ // A TableListField must be inside a form for its links to be generated
+ $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
+ $table
+ ), new FieldSet());
+
$items = $table->sourceItems();
$this->assertNotNull($items);
@@ -44,6 +54,11 @@ class TableListFieldTest extends SapphireTest {
"D" => "Col D",
"E" => "Col E",
));
+ // A TableListField must be inside a form for its links to be generated
+ $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
+ $table
+ ), new FieldSet());
+
$table->ShowPagination = true;
$table->PageSize = 2;
@@ -63,6 +78,11 @@ class TableListFieldTest extends SapphireTest {
"D" => "Col D",
"E" => "Col E",
));
+ // A TableListField must be inside a form for its links to be generated
+ $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
+ $table
+ ), new FieldSet());
+
$table->ShowPagination = true;
$table->PageSize = 2;
$_REQUEST['ctf']['Tester']['start'] = 2;
@@ -73,6 +93,46 @@ class TableListFieldTest extends SapphireTest {
$itemMap = $items->toDropdownMap("ID", "A") ;
$this->assertEquals(array(3 => "a3", 4 => "a4"), $itemMap);
}
+
+ function testCsvExport() {
+ $table = new TableListField("Tester", "TableListFieldTest_CsvExport", array(
+ "A" => "Col A",
+ "B" => "Col B"
+ ));
+
+ $form = new Form(new TableListFieldTest_TestController(), "TestForm", new FieldSet(
+ $table
+ ), new FieldSet());
+
+ $csvResponse = $table->export();
+
+ $csvOutput = $csvResponse->getBody();
+
+ $this->assertNotEquals($csvOutput, false);
+
+ // Create a temporary file and write the CSV to it.
+ $csvFileName = tempnam(TEMP_FOLDER, 'csv-export');
+ $csvFile = fopen($csvFileName, 'w');
+ fwrite($csvFile, $csvOutput);
+ fclose($csvFile);
+
+ $csvFile = fopen($csvFileName, 'r');
+ $csvRow = fgetcsv($csvFile);
+ $this->assertEquals(
+ $csvRow,
+ array('Col A', 'Col B')
+ );
+
+ $csvRow = fgetcsv($csvFile);
+ $this->assertEquals(
+ $csvRow,
+ array('"A field, with a comma"', 'A second field')
+ );
+
+ fclose($csvFile);
+
+ unlink($csvFileName);
+ }
}
class TableListFieldTest_Obj extends DataObject implements TestOnly {
@@ -89,5 +149,17 @@ class TableListFieldTest_Obj extends DataObject implements TestOnly {
function getE() {
return $this->A . '-e';
}
-
}
+
+class TableListFieldTest_CsvExport extends DataObject implements TestOnly {
+ static $db = array(
+ "A" => "Varchar",
+ "B" => "Varchar"
+ );
+}
+
+class TableListFieldTest_TestController extends Controller {
+ function Link() {
+ return "TableListFieldTest_TestController/";
+ }
+}
\ No newline at end of file
diff --git a/tests/forms/TableListFieldTest.yml b/tests/forms/TableListFieldTest.yml
index dc46ee327..18def3435 100644
--- a/tests/forms/TableListFieldTest.yml
+++ b/tests/forms/TableListFieldTest.yml
@@ -19,4 +19,8 @@ TableListFieldTest_Obj:
A: a5
B: b5
C: c5
-
\ No newline at end of file
+
+TableListFieldTest_CsvExport:
+ exportone:
+ A: "\"A field, with a comma\""
+ B: A second field
\ No newline at end of file
diff --git a/tests/security/SecurityTest.php b/tests/security/SecurityTest.php
index d2fc33dc2..08cac634b 100644
--- a/tests/security/SecurityTest.php
+++ b/tests/security/SecurityTest.php
@@ -10,6 +10,33 @@ class SecurityTest extends FunctionalTest {
protected $autoFollowRedirection = false;
+ protected $priorAuthenticators = array();
+
+ protected $priorDefaultAuthenticator = null;
+
+ function setUp() {
+ // This test assumes that MemberAuthenticator is present and the default
+ $this->priorAuthenticators = Authenticator::get_authenticators();
+ $this->priorDefaultAuthenticator = Authenticator::get_default_authenticator();
+
+ Authenticator::register('MemberAuthenticator');
+ Authenticator::set_default_authenticator('MemberAuthenticator');
+
+ parent::setUp();
+ }
+
+ function tearDown() {
+ // Restore selected authenticator
+
+ // MemberAuthenticator might not actually be present
+ if(!in_array('MemberAuthenticator', $this->priorAuthenticators)) {
+ Authenticator::unregister('MemberAuthenticator');
+ }
+ Authenticator::set_default_authenticator($this->priorDefaultAuthenticator);
+
+ parent::tearDown();
+ }
+
/**
* Test that the login form redirects to the change password form after logging in with an expired password
*/
|