ENHANCEMENT: implemented tests for exporting csv files

This commit is contained in:
Will Rossiter 2010-09-08 10:35:43 +00:00
parent 99d4721c88
commit 503c5c3ff6
9 changed files with 368 additions and 148 deletions

View File

@ -13,7 +13,7 @@ class SubmittedForm extends DataObject {
);
static $has_many = array(
"FieldValues" => "SubmittedFormField"
"Values" => "SubmittedFormField"
);
/**
@ -22,11 +22,13 @@ class SubmittedForm extends DataObject {
*
*/
protected function onBeforeDelete() {
if($this->FieldValues()) {
foreach($this->FieldValues() as $value) {
if($this->Values()) {
foreach($this->Values() as $value) {
$value->delete();
}
}
parent::onBeforeDelete();
}
}

View File

@ -21,12 +21,12 @@ class SubmittedFormReportField extends FormField {
function Submissions() {
$pageStart = isset($_REQUEST['start']) && is_numeric($_REQUEST['start']) ? $_REQUEST['start'] : 0;
$pageLength = 10;
$items = $this->form->getRecord()->getComponents('Submissions', null, "\"Created\" DESC", null, "$pageStart,$pageLength");
$formId = $this->form->getRecord()->ID;
foreach(DB::query("SELECT COUNT(*) AS \"CountRows\" FROM \"SubmittedForm\" WHERE \"ParentID\" = $formId") as $r) $totalCount = $r['CountRows'];
$items->setPageLimits($pageStart, $pageLength, $totalCount);
$items->NextStart = $pageStart + $pageLength;
$items->PrevStart = $pageStart - $pageLength;
@ -61,100 +61,116 @@ class SubmittedFormReportField extends FormField {
*
* @return HTTPResponse / bool
*/
public function export() {
public function export($id = false) {
if($id && is_int($id)) {
$SQL_ID = $id;
}
else {
if(isset($_REQUEST['id'])) {
$SQL_ID = Convert::raw2sql($_REQUEST['id']);
}
else {
user_error("No UserDefinedForm Defined.", E_USER_ERROR);
return false;
}
}
$now = Date("Y-m-d_h.i.s");
$fileName = "export-$now.csv";
$separator = ",";
$udf = DataObject::get_by_id("UserDefinedForm", $SQL_ID);
// Get the UserDefinedForm to export data from the URL
$SQL_ID = (isset($_REQUEST['id'])) ? Convert::raw2sql($_REQUEST['id']) : false;
if($udf) {
$csvHeaderNames = array();
$csvHeaderTitle = array();
$submissions = $udf->Submissions();
if($submissions && $submissions->exists()) {
// Get all the submission IDs (so we know what names/titles to get - helps for sites with many UDF's)
$inClause = array();
foreach($submissions as $submission) {
$inClause[] = $submission->ID;
}
if($SQL_ID) {
$udf = DataObject::get_by_id("UserDefinedForm", $SQL_ID);
if($udf) {
$submissions = $udf->Submissions();
if($submissions && $submissions->Count() > 0) {
// Get all the submission IDs (so we know what names/titles to get - helps for sites with many UDF's)
$inClause = array();
foreach($submissions as $submission) {
$inClause[] = $submission->ID;
}
// Get the CSV header rows from the database
$tmp = DB::query("
SELECT DISTINCT \"SubmittedFormField\".\"ID\", \"Name\", \"Title\"
FROM \"SubmittedFormField\"
LEFT JOIN \"SubmittedForm\" ON \"SubmittedForm\".\"ID\" = \"SubmittedFormField\".\"ParentID\"
WHERE \"SubmittedFormField\".\"ParentID\" IN (" . implode(',', $inClause) . ")
GROUP BY \"Name\"
ORDER BY \"SubmittedFormField\".\"ID\"
");
// Get the CSV header rows from the database
$tmp = DB::query("SELECT DISTINCT \"SubmittedFormField\".\"ID\", \"Name\", \"Title\"
FROM \"SubmittedFormField\"
LEFT JOIN \"SubmittedForm\" ON \"SubmittedForm\".\"ID\" = \"SubmittedFormField\".\"ParentID\"
WHERE \"SubmittedFormField\".\"ParentID\" IN (" . implode(',', $inClause) . ")
GROUP BY \"Name\"
ORDER BY \"SubmittedFormField\".\"ID\"");
// Sort the Names and Titles from the database query into separate keyed arrays
foreach($tmp as $array) {
$csvHeaderNames[] = $array['Name'];
$csvHeaderTitle[] = $array['Title'];
}
// For every submission...
$i = 0;
foreach($submissions as $submission) {
// Get the rows for this submission (One row = one form field)
$dataRow = $submission->FieldValues();
$rows[$i] = array();
// For every row/field, get all the columns
foreach($dataRow as $column) {
// If the Name of this field is in the $csvHeaderNames array, get an array of all the places it exists
if($index = array_keys($csvHeaderNames, $column->Name)) {
if(is_array($index)) {
// Set the final output array for each index that we want to insert this value into
foreach($index as $idx) {
$rows[$i][$idx] = $column->Value;
}
$rows[$i]['Submitted'] = $submission->Created;
}
}
}
$i++;
}
// CSV header row
$csvData = '"' . implode('","', $csvHeaderTitle) . '"' . ',"Submitted"'."\n";
// For every row of data (one form submission = one row)
foreach($rows as $row) {
// Loop over all the names we can use
for($i=0;$i<count($csvHeaderNames);$i++) {
if(!isset($row[$i]) || !$row[$i]) $csvData .= '"",'; // If there is no data for this column, output it as blank instead
else {
$tmp = str_replace('"', '""', $row[$i]);
$csvData .= '"' . $tmp . '",';
}
}
// Start a new row for each submission (re-check we have 'Submitted' in this entry)
if(isset($row['Submitted'])) $csvData .= '"'.$row['Submitted'].'"'."\n";
else $csvData .= '\n';
}
} else {
user_error("No submissions to export.", E_USER_ERROR);
// Sort the Names and Titles from the database query into separate keyed arrays
foreach($tmp as $array) {
$csvHeaderNames[] = $array['Name'];
$csvHeaderTitle[] = $array['Title'];
}
if(class_exists('SS_HTTPRequest')) {
SS_HTTPRequest::send_file($csvData, $fileName)->output();
} else {
HTTPRequest::send_file($csvData, $fileName)->output();
// For every submission...
$i = 0;
foreach($submissions as $submission) {
// Get the rows for this submission (One row = one form field)
$dataRow = $submission->Values();
$rows[$i] = array();
// For every row/field, get all the columns
foreach($dataRow as $column) {
// If the Name of this field is in the $csvHeaderNames array, get an array of all the places it exists
if($index = array_keys($csvHeaderNames, $column->Name)) {
if(is_array($index)) {
// Set the final output array for each index that we want to insert this value into
foreach($index as $idx) {
$rows[$i][$idx] = $column->Value;
}
}
}
}
$rows[$i]['Submitted'] = $submission->Created;
$i++;
}
// CSV header row
$csvData = '"' . implode('","', $csvHeaderTitle) . '"' . ',"Submitted"'."\n";
// For every row of data (one form submission = one row)
foreach($rows as $row) {
for($i=0;$i<count($csvHeaderNames);$i++) {
if(!isset($row[$i]) || !$row[$i]) $csvData .= '"",'; // If there is no data for this column, output it as blank instead
else {
$tmp = str_replace('"', '""', $row[$i]);
$csvData .= '"' . $tmp . '",';
}
}
// Start a new row for each submission (re-check we have 'Submitted' in this entry)
if(isset($row['Submitted'])) $csvData .= '"'.$row['Submitted'].'"'."\n";
else $csvData .= "\n";
}
} else {
user_error("'$SQL_ID' is a valid type, but we can't find a UserDefinedForm in the database that matches the ID.", E_USER_ERROR);
user_error("No submissions to export.", E_USER_ERROR);
}
if(SapphireTest::is_running_test()) {
return $csvData;
}
else {
SS_HTTPRequest::send_file($csvData, $fileName)->output();
}
} else {
user_error("'$SQL_ID' is not a valid UserDefinedForm ID.", E_USER_ERROR);
user_error("'$SQL_ID' is a valid type, but we can't find a UserDefinedForm in the database that matches the ID.", E_USER_ERROR);
}
}
@ -163,20 +179,28 @@ class SubmittedFormReportField extends FormField {
*
* @return Redirect|Boolean
*/
public function deletesubmissions() {
$SQL_ID = (isset($_REQUEST['id'])) ? Convert::raw2sql($_REQUEST['id']) : false;
if($SQL_ID) {
$udf = DataObject::get_by_id("UserDefinedForm", $SQL_ID);
$submissions = $udf->Submissions();
if($submissions) {
foreach($submissions as $submission) {
// delete the submission @see $submission->onBeforeDelete() for more info
$submission->delete();
}
return (Director::is_ajax()) ? true : Director::redirectBack();
public function deletesubmissions($id = false) {
if($id && is_int($id)) {
$SQL_ID = $id;
}
else {
if(isset($_REQUEST['id'])) {
$SQL_ID = Convert::raw2sql($_REQUEST['id']);
}
}
return (Director::is_ajax()) ? false : Director::redirectBack();
if(isset($SQL_ID)) {
$udf = DataObject::get_by_id("UserDefinedForm", $SQL_ID);
$submissions = $udf->Submissions();
if($submissions) {
foreach($submissions as $submission) {
$submission->delete();
}
return (Director::is_ajax() || SapphireTest::is_running_test()) ? true : Director::redirectBack();
}
}
return (Director::is_ajax() || SapphireTest::is_running_test()) ? false : Director::redirectBack();
}
/**
@ -184,16 +208,24 @@ class SubmittedFormReportField extends FormField {
*
* @return Redirect|Boolean
*/
public function deletesubmission() {
$SQL_ID = (isset($_REQUEST['id'])) ? Convert::raw2sql($_REQUEST['id']) : false;
if($SQL_ID) {
public function deletesubmission($id = false) {
if($id && is_int($id)) {
$SQL_ID = $id;
}
else {
if(isset($_REQUEST['id'])) {
$SQL_ID = Convert::raw2sql($_REQUEST['id']);
}
}
if(isset($SQL_ID)) {
$submission = DataObject::get_by_id("SubmittedForm", $SQL_ID);
if($submission) {
$submission->delete();
return (Director::is_ajax()) ? true : Director::redirectBack();
return (Director::is_ajax() || SapphireTest::is_running_test()) ? true : Director::redirectBack();
}
}
return (Director::is_ajax()) ? false : Director::redirectBack();
return (Director::is_ajax() || SapphireTest::is_running_test()) ? false : Director::redirectBack();
}
}

View File

@ -69,10 +69,11 @@
* we let the href of the delete link to do all the work for us
*/
$("#FormSubmissions .deleteSubmission").click(function() {
$("#userforms-submissions .deleteSubmission").click(function() {
var deletedSubmission = $(this);
$.post($(this).attr('href'), function(data) {
deletedSubmission.parents('div.report').fadeOut();
deletedSubmission.parents('div.userform-submission').fadeOut();
});
return false;
});

View File

@ -1,18 +1,17 @@
<div class="reports" id="FormSubmissions">
<div id="userforms-submissions">
<% if Submissions %>
<ul class="formSubmissionActions">
<!-- @todo work out why url_handlers dont like /export/2 -->
<ul class="userforms-submission-actions">
<li><a href="{$Top.Link}/export/?id={$RecordID}"><% _t('EXPORTSUBMISSIONS', 'Export submissions to CSV') %></a></li>
<li><a href="{$Top.Link}/deletesubmissions/?id={$RecordID}" class="deleteSubmission"><% _t('DELETEALLSUBMISSIONS', 'Delete All Submissions') %></a></li>
</ul>
<% control Submissions %>
<div class="report">
<h4 class="submitted"><% _t('SUBMITTED', 'Submitted at') %> $Created.Nice. <a href="{$Top.Link}/deletesubmission/?id={$ID}" class="deleteSubmission"><% _t('DELETESUBMISSION', 'Delete Submission') %></a></h4>
<div class="userform-submission">
<h4><% _t('SUBMITTED', 'Submitted at') %> $Created.Nice. <a href="{$Top.Link}/deletesubmission/?id={$ID}" class="deleteSubmission"><% _t('DELETESUBMISSION', 'Delete Submission') %></a></h4>
<table>
<% control FieldValues %>
<% control Values %>
<tr>
<td class="field">$Title</td>
<td class="value">$FormattedValue</td>
@ -21,8 +20,10 @@
</table>
</div>
<% end_control %>
<% if Submissions.MoreThanOnePage %>
<div class="pagination">
<div class="userforms-submissions-pagination">
<% if Submissions.NotFirstPage %>
<a class="prev" href="javascript:void(0)" onclick="jQuery('.middleColumn').parent().load(jQuery('base').get(0).href+'/{$Top.Link}/getSubmissions/?start={$Submissions.PrevStart}');" title="View the previous page">Previous page</a>
<% end_if %>
@ -35,5 +36,6 @@
</div>
<% end_if %>
<% end_if %>
<p class="noSubmissions" <% if Submissions %>style="display: none"<% end_if %>><% _t('NOSUBMISSIONS', 'No Submissions') %></p>
<p class="userforms-nosubmissions" <% if Submissions %>style="display: none"<% end_if %>><% _t('NOSUBMISSIONS', 'No Submissions') %></p>
</div>

View File

@ -193,12 +193,6 @@ class EditableFormFieldTest extends FunctionalTest {
function testEditableDropdownField() {
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown');
$option1 = $this->objFromFixture('EditableOption', 'option-1');
$option2 = $this->objFromFixture('EditableOption', 'option-2');
$dropdown->Options()->add($option1);
$dropdown->Options()->add($option2);
$field = $dropdown->getFormField();
@ -211,19 +205,13 @@ class EditableFormFieldTest extends FunctionalTest {
function testEditableRadioField() {
$radio = $this->objFromFixture('EditableRadioField', 'radio-field');
$option1 = $this->objFromFixture('EditableOption', 'option-1');
$option2 = $this->objFromFixture('EditableOption', 'option-2');
$radio->Options()->add($option1);
$radio->Options()->add($option2);
$field = $radio->getFormField();
$this->assertThat($field, $this->isInstanceOf('OptionsetField'));
$values = $field->getSource();
$this->assertEquals(array('Option 1' => 'Option 1', 'Option 2' => 'Option 2'), $values);
$this->assertEquals(array('Option 5' => 'Option 5', 'Option 6' => 'Option 6'), $values);
}
function testTitleField() {
@ -255,4 +243,59 @@ class EditableFormFieldTest extends FunctionalTest {
$this->assertEquals($text->getSettingFieldName('Foo'), "Fields[". $text->ID ."][CustomSettings][Foo]");
}
function testMultipleOptionDuplication() {
$dropdown = $this->objFromFixture('EditableDropdown','basic-dropdown');
$clone = $dropdown->duplicate();
$this->assertEquals($clone->Options()->Count(), $dropdown->Options()->Count());
foreach($clone->Options() as $option) {
$orginal = $dropdown->Options()->find('Title', $option->Title);
$this->assertEquals($orginal->Sort, $option->Sort);
}
}
function testMultipleOptionPopulateFromPostData() {
$dropdown = $this->objFromFixture('EditableDropdown','basic-dropdown');
$data = array();
foreach($dropdown->Options() as $option) {
$orginal[$option->ID] = array(
'Title' => $option->Title,
'Sort' => $option->Sort
);
$data[$option->ID] = array(
'Title' => 'New - '. $option->Title,
'Sort' => $option->Sort + 1
);
}
$dropdown->populateFromPostData($data);
$count = $dropdown->Options()->Count();
foreach($dropdown->Options() as $option) {
$this->assertEquals($option->Title, 'New - '. $orginal[$option->ID]['Title']);
$this->assertEquals($option->Sort, $orginal[$option->ID]['Sort'] + 1);
}
unset($data[1]);
$dropdown->populateFromPostData($data);
$this->assertEquals($dropdown->Options()->Count(), $count-1);
}
function testEditableTextFieldConfiguration() {
$text = $this->objFromFixture('EditableTextField', 'basic-text');
$configuration = $text->getFieldConfiguration();
Debug::show($configuration);
}
}

View File

@ -28,7 +28,7 @@ class FieldEditorTest extends FunctionalTest {
function testSaveInto() {
$this->logInWithPermission('ADMIN');
// @todo
}
function testAddField() {

View File

@ -4,27 +4,105 @@ class SubmittedFormTest extends FunctionalTest {
static $fixture_file = 'userforms/tests/SubmittedFormTest.yml';
function testReportSubmissions() {
protected $controller, $form, $page, $field;
function setUp() {
parent::setUp();
$this->page = $this->objFromFixture('UserDefinedForm', 'popular-form');
$this->controller = new SubmittedFormTest_Controller($this->page);
$this->form = $this->controller->Form();
$this->field = $this->form->dataFieldByName('Report');
}
function testCSVExport() {
function testSubmissions() {
$submissions = $this->field->Submissions();
// test with 11 submissions. Should be over 2 pages. 10 per page.
// @todo add tests to ensure the order
$this->assertEquals($submissions->Count(), 10);
$this->assertEquals($submissions->TotalPages(), 2);
$this->assertEquals($submissions->TotalItems(), 11);
}
function testGetSubmissionns() {
$template = $this->field->getSubmissions();
$parser = new CSSContentParser($template);
// check to ensure that the pagination exists
$pagination = $parser->getBySelector('.userforms-submissions-pagination');
$this->assertEquals(str_replace("\n", ' ',(string) $pagination[0]->span), "Viewing rows 0 - 10 of 11 rows");
$this->assertEquals(str_replace("\n", ' ',(string) $pagination[0]->a), "Next page");
// ensure the actions exist
$actions = $parser->getBySelector('.userforms-submission-actions');
$this->assertEquals(count($actions[0]->li), 2);
// submissions
$submissions = $parser->getBySelector('.userform-submission');
$this->assertEquals(count($submissions), 10);
}
function testCSVExport() {
$export = $this->field->export($this->page->ID);
// export it back to an array (rather than string)
$exportLines = explode("\n", $export);
$data = array();
array_pop($exportLines);
foreach($exportLines as $line) {
$line = explode("\",\"", $line);
$clean = array();
foreach($line as $part) {
$clean[] = trim($part, "\"");
}
$data[] = $clean;
}
// check the headers are fine
$this->assertEquals($data[0], array(
'Submitted Title','Submitted Title 2','Submitted'
));
// check the number of records in the export
$this->assertEquals(count($data), 12);
$this->assertEquals($data[1][0], 'Value 1');
$this->assertEquals($data[1][1], "");
$this->assertEquals($data[2][0], "");
$this->assertEquals($data[2][1], 'Value 2');
}
function testdeletesubmission() {
$submission = $this->objFromFixture('SubmittedForm', 'long-1');
$count = $this->page->Submissions()->Count();
$this->assertTrue($this->field->deletesubmission($submission->ID));
$this->assertEquals($count - 1, $this->page->Submissions()->Count());
$this->assertFalse($this->field->deletesubmission(-1));
}
function testdeletesubmissions() {
$this->assertTrue($this->field->deletesubmissions($this->page->ID));
$this->assertEquals($this->page->Submissions()->Count(), 0);
}
function testOnBeforeDeleteOfForm() {
$field = $this->objFromFixture('SubmittedFormField', 'submitted-form-field-1');
$form = $field->Parent();
$this->assertEquals($form->FieldValues()->Count(), 2);
$this->assertEquals($form->Values()->Count(), 2);
$form->delete();
$fields = DataObject::get('SubmittedFormField', "ParentID = '$form->ID'");
@ -60,9 +138,17 @@ class SubmittedFormTest extends FunctionalTest {
}
class SubmittedFormTest_Controller extends Controller {
class SubmittedFormTest_Controller extends ContentController {
function ReportField() {
return new Form($this, 'ReportField', new FieldSet(new SubmittedFormReportField('Report'), new FieldSet()));
function Form() {
$form = new Form($this, 'Form', new FieldSet(new SubmittedFormReportField('Report')), new FieldSet(new FormAction('Submit')));
$form->loadDataFrom($this->data());
return $form;
}
function forTemplate() {
return $this->renderWith(array('ContentController'));
}
}

View File

@ -1,22 +1,49 @@
SubmittedForm:
submitted-form-1:
submitted-form-2:
long-1:
long-2:
long-3:
long-4:
long-5:
long-6:
long-7:
long-8:
long-9:
long-10:
long-11:
UserDefinedForm:
form-page:
Title: Form
Submissions: =>SubmittedForm.submitted-form-1
form-page-2:
Title: Second Form
Submissions: =>SubmittedForm.submitted-form-2
popular-form:
Title: Popular Form
Submissions: =>SubmittedForm.long-1, =>SubmittedForm.long-2, =>SubmittedForm.long-3, =>SubmittedForm.long-4, =>SubmittedForm.long-5, =>SubmittedForm.long-6, =>SubmittedForm.long-7, =>SubmittedForm.long-8, =>SubmittedForm.long-9, =>SubmittedForm.long-10, =>SubmittedForm.long-11
File:
uploaded-file:
Name: My File
Filename: my-file.jpg
SubmittedForm:
submitted-form-1:
Parent: =>UserDefinedForm.form-page
submitted-form-2:
Parent: =>UserDefinedForm.form-page-2
SubmittedFormField:
submitted-form-field-1:
Parent: =>SubmittedForm.submitted-form-1
@ -32,6 +59,18 @@ SubmittedFormField:
I am here testing
Testing until I cannot
I love my testing
long-submitted-1:
Name: Submitted Name
Title: Submitted Title
Parent: =>SubmittedForm.long-1
Value: Value 1
long-submitted-2:
Parent: =>SubmittedForm.long-2
Name: Submitted Name 2
Title: Submitted Title 2
Value: Value 2
SubmittedFileField:
submitted-file-1:

View File

@ -2,19 +2,34 @@ EditableOption:
option-1:
Name: Option1
Title: Option 1
Sort: 1
option-2:
Name: Option2
Title: Option 2
Sort: 2
department-1:
Name: dept1
Title: sales@example.com
department-2:
Name: dept2
Title: accounts@example.com
option-3:
Name: Option3
Title: Option 3
option-4:
Name: Option4
Title: Option 4
option-5:
Name: Option5
Title: Option 5
option-6:
Name: Option6
Title: Option 6
UserDefinedForm_EmailRecipient:
recipient-1:
@ -73,7 +88,7 @@ EditableCheckboxGroupField:
checkbox-group:
Name: check-box-group
Title: Check box group
Options: =>EditableOption.option-1, =>EditableOption.option-2
Options: =>EditableOption.option-3, =>EditableOption.option-4
EditableEmailField:
email-field:
@ -85,7 +100,7 @@ EditableRadioField:
radio-field:
Name: radio-option
Title: Radio Option
Options: =>EditableOption.option-1, =>EditableOption.option-2
Options: =>EditableOption.option-5, =>EditableOption.option-6
UserDefinedForm: