NEW Migrate submissions section to a GridField instance. (Fixes #94)

This commit is contained in:
Will Rossiter 2013-03-05 10:45:54 +13:00
parent 527b63786d
commit 8e252c88ed
8 changed files with 245 additions and 325 deletions

View File

@ -0,0 +1,117 @@
<?php
/**
* Extension to the build in SilverStripe {@link GridField} to allow for
* filtering {@link SubmittedForm} objects in the submissions tab by
* entering the value of a field
*
* @package userforms
*/
class UserFormsGridFieldFilterHeader extends GridFieldFilterHeader {
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if(!$this->checkDataType($gridField->getList())) {
return;
}
if($actionName === 'filter') {
$gridField->State->UserFormsGridField = array(
'filter' => isset($data['FieldNameFilter']) ? $data['FieldNameFilter'] : null,
'value' => isset($data['FieldValue']) ? $data['FieldValue'] : null
);
}
}
public function getHTMLFragments($gridField) {
$fields = new ArrayList();
$state = $gridField->State->UserFormsGridField;
$selectedField = $state->filter;
$selectedValue = $state->value;
// get the state of the grid field and populate the default values.
// retrieve a list of all the available form fields that have been
// submitted in this form.
$formFields = SubmittedFormField::get()
->filter(array(
"SubmittedForm.ParentID" => $gridField->getList()->column()
))
->leftJoin('SubmittedForm', 'SubmittedFormField.ParentID = SubmittedForm.ID')
->sort('Title', 'ASC')
->map('Name', 'Title');
// show dropdown of all the fields available from the submitted form fields
// that have been saved. Takes the titles from the currently live form.
$columnField = new DropdownField(
'FieldNameFilter',
'',
$formFields->toArray(),
$selectedField,
null,
_t('UserFormsGridFieldFilterHeader.FILTERSUBMISSIONS', 'Filter Submissions..')
);
$valueField = new TextField('FieldValue', '', $selectedValue);
$columnField->addExtraClass('ss-gridfield-sort');
$columnField->addExtraClass('no-change-track');
$valueField->addExtraClass('ss-gridfield-sort');
$valueField->addExtraClass('no-change-track');
$valueField->setAttribute(
'placeholder',
_t('UserFormsGridFieldFilterHeader.WHEREVALUEIS', 'where value is..'
));
$fields->push($columnField);
$fields->push($valueField);
$fields->push($actions = new FieldGroup(
GridField_FormAction::create($gridField, 'filter', false, 'filter', null)
->addExtraClass('ss-gridfield-button-filter')
->setAttribute('title', _t('GridField.Filter', "Filter"))
->setAttribute('id', 'action_filter_' . $gridField->getModelClass() . '_' . $columnField),
GridField_FormAction::create($gridField, 'reset', false, 'reset', null)
->addExtraClass('ss-gridfield-button-close')
->setAttribute('title', _t('GridField.ResetFilter', "Reset"))
->setAttribute('id', 'action_reset_' . $gridField->getModelClass() . '_' . $columnField)
)
);
$actions->addExtraClass('filter-buttons');
$actions->addExtraClass('no-change-track');
$forTemplate = new ArrayData(array(
'Fields' => $fields
));
return array(
'header' => $forTemplate->renderWith('GridFieldFilterHeader_Row')
);
}
public function getManipulatedData(GridField $gridField, SS_List $dataList) {
$state = $gridField->State;
if($filter = $state->UserFormsGridField->toArray()) {
if(isset($filter['filter']) && isset($filter['value'])) {
$dataList = $dataList->where(sprintf("
SELECT COUNT(*) FROM SubmittedFormField
WHERE (
ParentID = SubmittedForm.ID AND
Name = '%s' AND
Value LIKE '%s'
) > 0",
Convert::raw2sql($filter['filter']),
Convert::raw2sql($filter['value'])
));
}
}
return $dataList;
}
}

View File

@ -1,223 +0,0 @@
<?php
/**
* Displays a summary of instances of a form submitted to the website
*
* @package userforms
*/
class SubmittedFormReportField extends FormField {
public function Field($properties = array()) {
Requirements::css(FRAMEWORK_DIR . "/css/SubmittedFormReportField.css");
Requirements::javascript("userforms/javascript/UserForm.js");
return $this->renderWith("SubmittedFormReportField");
}
/**
* Return the submissions from the site
*
* @return PaginatedList
*/
public function getSubmissions($page = 1) {
$record = $this->form->getRecord();
$submissions = $record->getComponents('Submissions', null, "\"Created\" DESC");
$query = DB::query(sprintf("SELECT COUNT(*) AS \"CountRows\" FROM \"SubmittedForm\" WHERE \"ParentID\" = '%d'", $record->ID));
$totalCount = 0;
foreach($query as $r) {
$totalCount = $r['CountRows'];
}
$list = new PaginatedList($submissions);
$list->setCurrentPage($page);
$list->setPageLength(10);
$list->setTotalItems($totalCount);
return $list;
}
/**
* @return string
*/
public function getMoreSubmissions() {
$page = ($page = $this->request->getVar('page')) ? (int)$page : 1;
return $this->customise(new ArrayData(array(
'Submissions' => $this->getSubmissions($page)
)))->renderWith(array('SubmittedFormReportField'));
}
public function getExportLink() {
return $this->Link('export?id=' . $this->RecordID());
}
public function getDeleteSubmissionsLink() {
return $this->Link('deletesubmissions?id=' . $this->RecordID());
}
public function LinkContainsParameter() {
return strpos($this->Link(), '?') !== FALSE;
}
/**
* ID of this forms record
*
* @return int
*/
public function RecordID() {
return $this->form->getRecord()->ID;
}
/**
* Export each of the form submissions for this UserDefinedForm
* instance into a CSV file.
*
* In order to run this export function, the user must be
* able to edit the page, so we check canEdit()
*
* @return HTTPResponse / bool
*/
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);
if($udf) {
$submissions = $udf->Submissions("", "\"ID\"");
// Collect unique columns for use in the CSV.
// Do it separately as we need a fixed number of columns for the file.
// Include all fields that have ever existed in this form.
// Preserve the ordering: the most recent form setup should be dominant.
$inClause = array();
foreach($submissions as $submission) {
$inClause[] = $submission->ID;
}
$csvHeaders = DB::query("SELECT \"Name\" , \"Title\" FROM \"SubmittedFormField\"
LEFT JOIN \"SubmittedForm\" ON \"SubmittedForm\".\"ID\" = \"SubmittedFormField\".\"ParentID\"
WHERE \"SubmittedFormField\".\"ParentID\" IN (" . implode(',', $inClause) . ")
ORDER BY \"SubmittedFormField\".\"ParentID\" DESC, \"SubmittedFormField\".\"ID\"
");
if ($csvHeaders) $csvHeaders = $csvHeaders->map();
if($submissions && $submissions->exists()) {
$data = array();
// Create CSV rows out of submissions. Fields on those submissions will become columns.
foreach($submissions as $submission) {
$fields = $submission->Values();
$row = array();
foreach($fields as $field) {
$row[$field->Name] = $field->getExportValue();
}
$row['Submitted'] = $submission->Created;
$data[] = $row;
}
// Create the CSV header line first:
$csvData = '"' . implode('","', $csvHeaders) . '"' . ',"Submitted"'."\n";
// Now put the collected data under relevant columns
foreach($data as $row) {
$csvRowItems = array();
foreach ($csvHeaders as $columnName=>$columnTitle) {
if (!isset($row[$columnName])) $csvRowItems[] = ""; // This submission did not have that column, insert blank
else $csvRowItems[] = str_replace('"', '""', $row[$columnName]);
}
$csvRowItems[] = $row['Submitted'];
// Encode the row by hand (fputcsv fails to encode newlines properly)
if (count($csvRowItems)) $csvData .= "\"".implode($csvRowItems, "\",\"")."\"\n";
}
} else {
user_error("No submissions to export.", E_USER_ERROR);
}
if(class_exists('SapphireTest', false) && SapphireTest::is_running_test()) {
return $csvData;
}
else {
SS_HTTPRequest::send_file($csvData, $fileName, 'text/csv')->output();
exit;
}
} 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);
}
}
/**
* Delete all the submissions listed in the user defined form
*
* @return Redirect|Boolean
*/
public function deletesubmissions($id = false) {
$isRunningTests = (class_exists('SapphireTest', false) && SapphireTest::is_running_test());
if($id && is_int($id)) {
$SQL_ID = $id;
}
else {
if(isset($_REQUEST['id'])) {
$SQL_ID = Convert::raw2sql($_REQUEST['id']);
}
}
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() || $isRunningTests) ? true : Controller::curr()->redirectBack();
}
}
return (Director::is_ajax() || $isRunningTests) ? false : Controller::curr()->redirectBack();
}
/**
* Delete a given submission from a user defined form
*
* @return Redirect|Boolean
*/
public function deletesubmission($id = false) {
$isRunningTests = (class_exists('SapphireTest', false) && SapphireTest::is_running_test());
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() || $isRunningTests) ? true : Controller::curr()->redirectBack();
}
}
return (Director::is_ajax() || $isRunningTests) ? false : Controller::curr()->redirectBack();
}
}

View File

@ -50,34 +50,41 @@ class UserDefinedForm extends Page {
* @return FieldList
*/
public function getCMSFields() {
// call updateCMSFields after userforms
SiteTree::disableCMSFieldsExtensions();
$fields = parent::getCMSFields();
SiteTree::enableCMSFieldsExtensions();
// define tabs
$fields->findOrMakeTab('Root.FormContent', _t('UserDefinedForm.FORM', 'Form'));
$fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.FORMOPTIONS', 'Form Options'));
$fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.FORMSUBMISSIONS', 'Form Submissions'));
$fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.CONFIGURATION', 'Configuration'));
$fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions'));
// field editor
$fields->addFieldToTab("Root.FormContent", new FieldEditor("Fields", 'Fields', "", $this ));
// text to show on complete
$onCompleteFieldSet = new CompositeField(
$label=new LabelField('OnCompleteMessageLabel',_t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')),
$editor=new HtmlEditorField( "OnCompleteMessage", "", _t('UserDefinedForm.ONCOMPLETEMESSAGE', $this->OnCompleteMessage))
$label = new LabelField('OnCompleteMessageLabel',_t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')),
$editor = new HtmlEditorField( "OnCompleteMessage", "", _t('UserDefinedForm.ONCOMPLETEMESSAGE', $this->OnCompleteMessage))
);
$onCompleteFieldSet->addExtraClass('field');
$editor->setRows(3);
$label->addExtraClass('left');
UserDefinedForm_EmailRecipient::$summary_fields=array(
UserDefinedForm_EmailRecipient::$summary_fields = array(
'EmailAddress' => _t('UserDefinedForm.EMAILADDRESS', 'Email'),
'EmailSubject' => _t('UserDefinedForm.EMAILSUBJECT', 'Subject'),
'EmailFrom' => _t('UserDefinedForm.EMAILFROM', 'From')
);
// who do we email on submission
$emailRecipients = new GridField("EmailRecipients", "EmailRecipients", $this->EmailRecipients(), GridFieldConfig_RecordEditor::create(10));
$emailRecipients->getConfig()->getComponentByType('GridFieldAddNewButton')->setButtonName('Add Email Recipient');
$emailRecipients = new GridField("EmailRecipients", _t('UserDefinedForm.EMAILRECIPIENTS', 'Email Recipients'), $this->EmailRecipients(), GridFieldConfig_RecordEditor::create(10));
$emailRecipients->getConfig()->getComponentByType('GridFieldAddNewButton')->setButtonName(
_t('UserDefinedform.ADDEMAILRECIPIENT', 'Add Email Recipient')
);
$fields->addFieldsToTab("Root.FormOptions", $onCompleteFieldSet);
$fields->addFieldToTab("Root.FormOptions", $emailRecipients);
@ -85,9 +92,55 @@ class UserDefinedForm extends Page {
// view the submissions
$fields->addFieldToTab("Root.Submissions", new SubmittedFormReportField( "Reports", _t('UserDefinedForm.RECEIVED', 'Received Submissions'), "", $this ) );
$fields->addFieldToTab("Root.Submissions", new CheckboxField('DisableSaveSubmissions',_t('UserDefinedForm.SAVESUBMISSIONS',"Disable Saving Submissions to Server")));
$submissions = new GridField(
"Reports",
_t('UserDefinedform.SUBMISSIONS', 'Submissions'),
$this->Submissions()->sort('Created', 'DESC')
);
$config = new GridFieldConfig();
$config->addComponent(new GridFieldToolbarHeader());
$config->addComponent($sort = new GridFieldSortableHeader());
$config->addComponent($filter = new UserFormsGridFieldFilterHeader());
$config->addComponent(new GridFieldDataColumns());
$config->addComponent(new GridFieldEditButton());
$config->addComponent(new GridState_Component());
$config->addComponent(new GridFieldDeleteAction());
$config->addComponent(new GridFieldPageCount('toolbar-header-right'));
$config->addComponent($pagination = new GridFieldPaginator(25));
$config->addComponent(new GridFieldDetailForm());
$config->addComponent($export = new GridFieldExportButton());
$config->addComponent($print = new GridFieldPrintButton());
$sort->setThrowExceptionOnBadDataType(false);
$filter->setThrowExceptionOnBadDataType(false);
$pagination->setThrowExceptionOnBadDataType(false);
// attach every column to the print view from
$columns = SubmittedFormField::get()
->filter(array(
"SubmittedForm.ParentID" => $this->ID
))
->leftJoin('SubmittedForm', 'SubmittedFormField.ParentID = SubmittedForm.ID')
->map('Name', 'Title');
$columns = $columns->toArray();
$columns['Created'] = "Created";
// print configuration
$print->setPrintHasHeader(true);
$print->setPrintColumns($columns);
// export configuration
$export->setCsvHasHeader(true);
$export->setExportColumns($columns);
$submissions->setConfig($config);
$fields->addFieldToTab("Root.Submissions", $submissions);
$fields->addFieldToTab("Root.FormOptions", new CheckboxField('DisableSaveSubmissions',_t('UserDefinedForm.SAVESUBMISSIONS',"Disable Saving Submissions to Server")));
$this->extend('updateCMSFields', $fields);
return $fields;
}

View File

@ -8,8 +8,9 @@
class EditableFileField extends EditableFormField {
static $singular_name = 'File Upload Field';
static $plural_names = 'File Fields';
public static $singular_name = 'File Upload Field';
public static $plural_names = 'File Fields';
public function getFormField() {
$field = new FileField($this->Name, $this->Title);

View File

@ -15,14 +15,68 @@ class SubmittedForm extends DataObject {
public static $has_many = array(
"Values" => "SubmittedFormField"
);
public static $summary_fields = array(
'ID',
'Created'
);
/**
* Returns the value of a relation or, in the case of this form, the value
* of a given child {@link SubmittedFormField}
*
* @param string
*
* @return mixed
*/
public function relField($fieldName) {
// default case
if($value = parent::relField($fieldName)) {
return $value;
}
// check values for a form field with the matching name.
$formField = SubmittedFormField::get()->filter(array(
'ParentID' => $this->ID,
'Name' => $fieldName
))->first();
if($formField) {
return $formField->getFormattedValue();
}
}
/**
* @return FieldList
*/
public function getCMSFields() {
$fields = parent::getCMSFields();
$fields->removeByName('Values');
$values = new GridField(
"Values",
"SubmittedFormField",
$this->Values()->sort('Created', 'ASC')
);
$config = new GridFieldConfig();
$config->addComponent(new GridFieldDataColumns());
$config->addComponent(new GridFieldExportButton());
$config->addComponent(new GridFieldPrintButton());
$values->setConfig($config);
$fields->addFieldToTab('Root.Main', $values);
return $fields;
}
/**
* Before we delete this form make sure we delete all the
* field values so that we don't leave old data round
*
* @return void
*/
protected function onBeforeDelete() {
if($this->Values()) {
foreach($this->Values() as $value) {
$value->delete();
@ -31,9 +85,4 @@ class SubmittedForm extends DataObject {
parent::onBeforeDelete();
}
// Used in the CMS to join the links properly
public function DeleteLink($controllerLink) {
return Controller::join_links($controllerLink, 'deletesubmission?id=' . $this->ID);
}
}

View File

@ -7,16 +7,21 @@
class SubmittedFormField extends DataObject {
static $db = array(
public static $db = array(
"Name" => "Varchar",
"Value" => "Text",
"Title" => "Varchar(255)"
);
static $has_one = array(
public static $has_one = array(
"Parent" => "SubmittedForm"
);
public static $summary_fields = array(
'Title',
'FormattedValue' => 'Value'
);
/**
* Generate a formatted value for the reports and email notifications.
* Converts new lines (which are stored in the database text field) as

View File

@ -108,34 +108,6 @@
$.entwine('udf', function($){
/*--------------------- SUBMISSIONS ------------------------ */
/**
* Delete a given Submission from the form
*/
$("#userforms-submissions .deleteSubmission").live('click', function(event) {
event.preventDefault();
var deletedSubmission = $(this);
$.post($(this).attr('href'), function(data) {
deletedSubmission.parents('div.userform-submission').slideUp(function(){$(this).remove()});
});
});
/**
* Delete all submissions and fade them out if successful
*/
$("#userforms-submissions .deleteAllSubmissions").live('click', function(event) {
event.preventDefault();
if(!confirm(userforms.message('CONFIRM_DELETE_ALL_SUBMISSIONS'))) {
return;
}
var self = this;
$.post($(this).attr('href'), function(data) {
$(self).parents('#userforms-submissions').children().slideUp(function(){$(this).remove()})
});
});
/*-------------------- FIELD EDITOR ----------------------- */
/**
@ -366,16 +338,6 @@
currentRules.append(newRule);
}
});
$('.userforms-submissions-pagination a').entwine({
onclick: function(e) {
e.preventDefault();
$.get($(this).attr('href'), function(data) {
$('#userforms-submissions').replaceWith(data);
});
this._super();
}
})
});
});
})(jQuery);

View File

@ -1,44 +0,0 @@
<div id="userforms-submissions">
<% if Submissions %>
<ul class="userforms-submission-actions">
<li><a href="$ExportLink"><% _t('EXPORTSUBMISSIONS', 'Export submissions to CSV') %></a></li>
<li><a href="$DeleteSubmissionsLink" class="deleteAllSubmissions"><% _t('DELETEALLSUBMISSIONS', 'Delete All Submissions') %></a></li>
</ul>
<h5>Name: $Name</h5>
<% loop Submissions %>
<div class="userform-submission">
<h5><% _t('SUBMITTED', 'Submitted at') %> {$Created.Nice} <a href="$DeleteLink($Up.Link)" class="deleteSubmission"><% _t('DELETESUBMISSION', 'Delete Submission') %></a></h5>
<% loop Values %>
<div id="Text_readonly" class="field readonly text">
<label class="left" for="Form_EditForm_Text_readonly">$Title</label>
<div class="middleColumn">
<span id="Form_EditForm_Text_readonly" class="readonly text">$FormattedValue</span>
</div>
</div>
<% end_loop %>
</div>
<% end_loop %>
<% if Submissions.MoreThanOnePage %>
<div class="userforms-submissions-pagination">
<span><% _t('PAGES', 'Pages') %>:</span>
<% loop Submissions.Pages() %>
<% if CurrentBool %>
$PageNum
<% else %>
<% if Link %>
<a href="{$Top.Link(getMoreSubmissions)}<% if $Top.LinkContainsParameter %>&<% else %>?<% end_if %>page=$PageNum">$PageNum</a>
<% else %>
...
<% end_if %>
<% end_if %>
<% end_loop %>
</div>
<% end_if %>
<% else %>
<p class="userforms-nosubmissions" <% if Submissions %>style="display: none"<% end_if %>><% _t('NOSUBMISSIONS', 'No Submissions') %></p>
<% end_if %>
</div>