mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
ENHANCEMENT: added test coverage for UserDefinedForm.php and placeholders for other sections. API change: processNewFormFields removed
API change: refactored Form() into getFormFields(), getFormActions(), getRequiredFields() ENHANCEMENT: added updateForm* extension hooks to allow customization of userforms BUGFIX: gave custom scripts their own ID to enable it to be blocked.
This commit is contained in:
parent
5f4d4e7d28
commit
3c1d81d014
@ -19,7 +19,7 @@ class UserDefinedForm extends Page {
|
|||||||
/**
|
/**
|
||||||
* @var String What level permission is needed to edit / add
|
* @var String What level permission is needed to edit / add
|
||||||
*/
|
*/
|
||||||
static $need_permission = 'ADMIN';
|
static $need_permission = array('ADMIN');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var String Required Identifier
|
* @var String Required Identifier
|
||||||
@ -68,6 +68,7 @@ class UserDefinedForm extends Page {
|
|||||||
|
|
||||||
// define tabs
|
// define tabs
|
||||||
$fields->findOrMakeTab('Root.Content.Form', _t('UserDefinedForm.FORM', 'Form'));
|
$fields->findOrMakeTab('Root.Content.Form', _t('UserDefinedForm.FORM', 'Form'));
|
||||||
|
$fields->findOrMakeTab('Root.Content.Options', _t('UserDefinedForm.OPTIONS', 'Options'));
|
||||||
$fields->findOrMakeTab('Root.Content.EmailRecipients', _t('UserDefinedForm.EMAILRECIPIENTS', 'Email Recipients'));
|
$fields->findOrMakeTab('Root.Content.EmailRecipients', _t('UserDefinedForm.EMAILRECIPIENTS', 'Email Recipients'));
|
||||||
$fields->findOrMakeTab('Root.Content.OnComplete', _t('UserDefinedForm.ONCOMPLETE', 'On Complete'));
|
$fields->findOrMakeTab('Root.Content.OnComplete', _t('UserDefinedForm.ONCOMPLETE', 'On Complete'));
|
||||||
$fields->findOrMakeTab('Root.Content.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions'));
|
$fields->findOrMakeTab('Root.Content.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions'));
|
||||||
@ -102,6 +103,7 @@ class UserDefinedForm extends Page {
|
|||||||
);
|
);
|
||||||
|
|
||||||
$fields->addFieldsToTab("Root.Content.OnComplete", $onCompleteFieldSet);
|
$fields->addFieldsToTab("Root.Content.OnComplete", $onCompleteFieldSet);
|
||||||
|
$fields->addFieldsToTab("Root.Content.Options", $this->getFormOptions());
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
@ -155,19 +157,61 @@ class UserDefinedForm extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Roll back a form to a previous version
|
* Roll back a form to a previous version.
|
||||||
*
|
*
|
||||||
* @param String|int Version to roll back to
|
* @param String|int Version to roll back to
|
||||||
*/
|
*/
|
||||||
public function doRollbackTo($version) {
|
public function doRollbackTo($version) {
|
||||||
|
parent::doRollbackTo($version);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Not implemented yet
|
||||||
|
|
||||||
|
// get the older version
|
||||||
|
$reverted = Versioned::get_version($this->ClassName, $this->ID, $version);
|
||||||
|
|
||||||
|
if($reverted) {
|
||||||
|
|
||||||
|
// using the lastedited date of the reverted object we can work out which
|
||||||
|
// form fields to revert back to
|
||||||
if($this->Fields()) {
|
if($this->Fields()) {
|
||||||
foreach($this->Fields() as $field) {
|
foreach($this->Fields() as $field) {
|
||||||
$field->publish($version, "Stage", true);
|
// query to see when the version of the page was pumped
|
||||||
|
$editedDate = DB::query("
|
||||||
|
SELECT LastEdited
|
||||||
|
FROM \"SiteTree_versions\"
|
||||||
|
WHERE \"RecordID\" = '$this->ID' AND \"Version\" = $version
|
||||||
|
")->value();
|
||||||
|
|
||||||
|
|
||||||
|
// find a the latest version which has been edited
|
||||||
|
$versionToGet = DB::query("
|
||||||
|
SELECT *
|
||||||
|
FROM \"EditableFormField_versions\"
|
||||||
|
WHERE \"RecordID\" = '$field->ID' AND \"LastEdited\" <= '$editedDate'
|
||||||
|
ORDER BY Version DESC
|
||||||
|
LIMIT 1
|
||||||
|
")->record();
|
||||||
|
|
||||||
|
if($versionToGet) {
|
||||||
|
Debug::show('publishing field'. $field->Name);
|
||||||
|
Debug::show($versionToGet);
|
||||||
|
$field->publish($versionToGet, "Stage", true);
|
||||||
$field->writeWithoutVersion();
|
$field->writeWithoutVersion();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Debug::show('deleting field'. $field->Name);
|
||||||
|
$this->Fields()->remove($field);
|
||||||
|
|
||||||
|
$field->delete();
|
||||||
|
$field->destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::doRollbackTo($version);
|
// @todo Emails
|
||||||
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -178,7 +222,8 @@ class UserDefinedForm extends Page {
|
|||||||
public function doRevertToLive() {
|
public function doRevertToLive() {
|
||||||
if($this->Fields()) {
|
if($this->Fields()) {
|
||||||
foreach($this->Fields() as $field) {
|
foreach($this->Fields() as $field) {
|
||||||
$field->writeToStage('Live', 'Stage');
|
$field->publish("Live", "Stage", false);
|
||||||
|
$field->writeWithoutVersion();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,16 +261,22 @@ class UserDefinedForm extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom Form Actions for the form
|
* Custom options for the form. You can extend the built in options by
|
||||||
|
* using {@link updateFormOptions()}
|
||||||
*
|
*
|
||||||
* @param bool Is the Form readonly
|
|
||||||
* @return FieldSet
|
* @return FieldSet
|
||||||
*/
|
*/
|
||||||
public function customFormActions($isReadonly = false) {
|
public function getFormOptions() {
|
||||||
return new FieldSet(
|
$submit = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
|
||||||
new TextField("SubmitButtonText", _t('UserDefinedForm.TEXTONSUBMIT', 'Text on submit button:'), $this->SubmitButtonText),
|
|
||||||
|
$options = new FieldSet(
|
||||||
|
new TextField("SubmitButtonText", _t('UserDefinedForm.TEXTONSUBMIT', 'Text on submit button:'), $submit),
|
||||||
new CheckboxField("ShowClearButton", _t('UserDefinedForm.SHOWCLEARFORM', 'Show Clear Form Button'), $this->ShowClearButton)
|
new CheckboxField("ShowClearButton", _t('UserDefinedForm.SHOWCLEARFORM', 'Show Clear Form Button'), $this->ShowClearButton)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$this->extend('updateFormOptions', $options);
|
||||||
|
|
||||||
|
return $options;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -286,69 +337,168 @@ class UserDefinedForm_Controller extends Page_Controller {
|
|||||||
|
|
||||||
return array(
|
return array(
|
||||||
'Content' => DBField::create('HTMLText', $this->Content),
|
'Content' => DBField::create('HTMLText', $this->Content),
|
||||||
'Form' => $this->Form
|
'Form' => $this->Form()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User Defined Form. Feature of the user defined form is if you want the
|
* Get the form for the page. Form can be modified by calling {@link updateForm()}
|
||||||
* form to appear in a custom location on the page you can use $UserDefinedForm
|
* on a UserDefinedForm extension
|
||||||
* in the content area to describe where you want the form
|
|
||||||
*
|
|
||||||
* @todo Abstract the Conditional Logic from the Form. This should be tied
|
|
||||||
* to the EditableFormField class so that fields (eg checkboxes)
|
|
||||||
* can define their own logic
|
|
||||||
*
|
*
|
||||||
* @return Form
|
* @return Form
|
||||||
*/
|
*/
|
||||||
public function Form() {
|
function Form() {
|
||||||
|
$fields = $this->getFormFields();
|
||||||
|
$actions = $this->getFormActions();
|
||||||
|
|
||||||
|
// get the required fields including the validation
|
||||||
|
$required = $this->getRequiredFields();
|
||||||
|
|
||||||
|
// generate the conditional logic
|
||||||
|
$this->generateConditionalJavascript();
|
||||||
|
|
||||||
|
$form = new Form($this, "Form", $fields, $actions, $required);
|
||||||
|
|
||||||
|
$this->extend('updateForm', $form);
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the form fields for the form on this page. Can modify this FieldSet
|
||||||
|
* by using {@link updateFormFields()} on an {@link Extension} subclass which
|
||||||
|
* is applied to this controller
|
||||||
|
*
|
||||||
|
* @return FieldSet
|
||||||
|
*/
|
||||||
|
function getFormFields() {
|
||||||
$fields = new FieldSet();
|
$fields = new FieldSet();
|
||||||
$fieldValidation = array();
|
|
||||||
$fieldValidationRules = array();
|
if($this->Fields()) {
|
||||||
$customDisplayRules = "";
|
foreach($this->Fields() as $editableField) {
|
||||||
$defaults = "";
|
// get the raw form field from the editable version
|
||||||
$this->SubmitButtonText = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
|
$field = $editableField->getFormField();
|
||||||
|
if(!$field) break;
|
||||||
|
|
||||||
|
// set the error / formatting messages
|
||||||
|
$title = strip_tags("'". ($editableField->Title ? $editableField->Title : $editableField->Name) . "'");
|
||||||
|
|
||||||
|
$errorMessage = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', $title);
|
||||||
|
$errorMessage = ($editableField->CustomErrorMessage) ? $editableField->CustomErrorMessage : $errorMessage;
|
||||||
|
|
||||||
|
$field->setCustomValidationMessage($errorMessage);
|
||||||
|
|
||||||
|
// set the right title on this field
|
||||||
|
if($right = $editableField->getSetting('RightTitle')) {
|
||||||
|
$field->setRightTitle($right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this field is required add some
|
||||||
|
if($editableField->Required) {
|
||||||
|
$field->addExtraClass('requiredField');
|
||||||
|
|
||||||
|
if($identifier = UserDefinedForm::$required_identifier) {
|
||||||
|
|
||||||
|
$title = $field->Title() ." <span class='required-identifier'>". $identifier . "</span>";
|
||||||
|
$field->setTitle($title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields->push($field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extend('updateFormFields', $fields);
|
||||||
|
|
||||||
|
return $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the form actions for the UserDefinedForm. You
|
||||||
|
* can manipulate these by using {@link updateFormActions()} on
|
||||||
|
* a decorator.
|
||||||
|
*
|
||||||
|
* @todo Make form actions editable via their own field editor.
|
||||||
|
*
|
||||||
|
* @return FieldSet
|
||||||
|
*/
|
||||||
|
function getFormActions() {
|
||||||
|
$submitText = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
|
||||||
|
|
||||||
|
$actions = new FieldSet(
|
||||||
|
new FormAction("process", $submitText)
|
||||||
|
);
|
||||||
|
|
||||||
|
if($this->ShowClearButton) {
|
||||||
|
$actions->push(new ResetFormAction("clearForm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extend('updateFormActions', $actions);
|
||||||
|
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the required form fields for this form. Includes building the jQuery
|
||||||
|
* validate structure
|
||||||
|
*
|
||||||
|
* @return RequiredFields
|
||||||
|
*/
|
||||||
|
function getRequiredFields() {
|
||||||
|
$required = new RequiredFields();
|
||||||
|
|
||||||
|
$rules = array();
|
||||||
|
$validation = array();
|
||||||
|
|
||||||
if($this->Fields()) {
|
if($this->Fields()) {
|
||||||
foreach($this->Fields() as $field) {
|
foreach($this->Fields() as $field) {
|
||||||
|
|
||||||
$fieldToAdd = $field->getFormField();
|
|
||||||
|
|
||||||
if(!$fieldToAdd) break;
|
|
||||||
|
|
||||||
$fieldValidationOptions = array();
|
|
||||||
|
|
||||||
// Set the Error Messages
|
|
||||||
$errorMessage = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', strip_tags("'". ($field->Title ? $field->Title : $field->Name) . "'"));
|
|
||||||
$errorMessage = ($field->CustomErrorMessage) ? $field->CustomErrorMessage : $errorMessage;
|
|
||||||
$fieldToAdd->setCustomValidationMessage($errorMessage);
|
|
||||||
|
|
||||||
// Set the right title on this field
|
|
||||||
if($right = $field->getSetting('RightTitle')) {
|
|
||||||
$fieldToAdd->setRightTitle($right);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is this field required
|
|
||||||
if($field->Required) {
|
if($field->Required) {
|
||||||
$fieldValidation[$field->Name] = $errorMessage;
|
|
||||||
$fieldValidationOptions['required'] = true;
|
$validation[$field->Name] = $field->getFormField()->getCustomValidationMessage();
|
||||||
$fieldToAdd->addExtraClass('requiredField');
|
$rules[$field->Name] = array_merge(array('required'), $field->getValidation());
|
||||||
if(UserDefinedForm::$required_identifier) {
|
|
||||||
$title = $fieldToAdd->Title() ." <span class='requiredIdentifier'>". UserDefinedForm::$required_identifier . "</span>";
|
$required->addRequiredField($field->Name);
|
||||||
$fieldToAdd->setTitle($title);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add field to the form
|
// Set the Form Name
|
||||||
$fields->push($fieldToAdd);
|
$rules = $this->array2json($rules);
|
||||||
|
$messages = $this->array2json($validation);
|
||||||
|
|
||||||
// Ask our form field for some more information on hour it should be validated
|
// set the custom script for this form
|
||||||
$fieldValidationOptions = array_merge($fieldValidationOptions, $field->getValidation());
|
Requirements::customScript(<<<JS
|
||||||
|
(function($) {
|
||||||
|
$(document).ready(function() {
|
||||||
|
$("#Form_Form").validate({
|
||||||
|
errorClass: "required",
|
||||||
|
messages:
|
||||||
|
$messages
|
||||||
|
,
|
||||||
|
rules:
|
||||||
|
$rules
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})(jQuery);
|
||||||
|
JS
|
||||||
|
, 'UserFormsValidation');
|
||||||
|
|
||||||
// Check if we have need to update the global validation
|
$this->extend('updateRequiredFields', $required);
|
||||||
if($fieldValidationOptions) {
|
|
||||||
$fieldValidationRules[$field->Name] = $fieldValidationOptions;
|
return $required;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the javascript for the conditional field show / hiding logic.
|
||||||
|
* Allows complex rules to be created
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function generateConditionalJavascript() {
|
||||||
|
$default = "";
|
||||||
|
$rules = "";
|
||||||
|
|
||||||
|
if($this->Fields()) {
|
||||||
|
foreach($this->Fields() as $field) {
|
||||||
$fieldId = $field->Name;
|
$fieldId = $field->Name;
|
||||||
|
|
||||||
if($field->ClassName == 'EditableFormHeading') {
|
if($field->ClassName == 'EditableFormHeading') {
|
||||||
@ -356,8 +506,8 @@ class UserDefinedForm_Controller extends Page_Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Is this Field Show by Default
|
// Is this Field Show by Default
|
||||||
if(!$field->ShowOnLoad) {
|
if(!$field->getShowOnLoad()) {
|
||||||
$defaults .= "$(\"#" . $fieldId . "\").hide();\n";
|
$default .= "$(\"#" . $fieldId . "\").hide();\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for field dependencies / default
|
// Check for field dependencies / default
|
||||||
@ -438,7 +588,7 @@ class UserDefinedForm_Controller extends Page_Controller {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// put it all together
|
// put it all together
|
||||||
$customDisplayRules .= $fieldToWatch.".$action(function() {
|
$rules .= $fieldToWatch.".$action(function() {
|
||||||
if(". $expression ." ) {
|
if(". $expression ." ) {
|
||||||
$(\"#". $fieldId ."\").".$view."();
|
$(\"#". $fieldId ."\").".$view."();
|
||||||
}
|
}
|
||||||
@ -451,53 +601,18 @@ class UserDefinedForm_Controller extends Page_Controller {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
|
|
||||||
|
|
||||||
// Keep track of the referer
|
|
||||||
$fields->push( new HiddenField( "Referrer", "", $referer ) );
|
|
||||||
|
|
||||||
// Build actions
|
|
||||||
$actions = new FieldSet(
|
|
||||||
new FormAction("process", $this->SubmitButtonText)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Do we want to add a clear form.
|
|
||||||
if($this->ShowClearButton) {
|
|
||||||
$actions->push(new ResetFormAction("clearForm"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the form
|
|
||||||
$form = new Form($this, "Form", $fields, $actions, new RequiredFields(array_keys($fieldValidation)));
|
|
||||||
$form->loadDataFrom($this->failover);
|
|
||||||
|
|
||||||
$FormName = $form->FormName();
|
|
||||||
|
|
||||||
// Set the Form Name
|
|
||||||
$rules = $this->array2json($fieldValidationRules);
|
|
||||||
$messages = $this->array2json($fieldValidation);
|
|
||||||
|
|
||||||
|
|
||||||
// set the custom script for this form
|
|
||||||
Requirements::customScript(<<<JS
|
Requirements::customScript(<<<JS
|
||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$defaults
|
|
||||||
$("#$FormName").validate({
|
|
||||||
errorClass: "required",
|
|
||||||
messages:
|
|
||||||
$messages
|
|
||||||
,
|
|
||||||
|
|
||||||
rules:
|
|
||||||
$rules
|
$rules
|
||||||
});
|
|
||||||
$customDisplayRules
|
$default
|
||||||
});
|
})
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
JS
|
JS
|
||||||
);
|
, 'UserFormsConditional');
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -507,15 +622,15 @@ JS
|
|||||||
* @param Array array to convert
|
* @param Array array to convert
|
||||||
* @return JSON
|
* @return JSON
|
||||||
*/
|
*/
|
||||||
protected function array2json($array) {
|
function array2json($array) {
|
||||||
foreach($array as $key => $value)
|
foreach($array as $key => $value)
|
||||||
if(is_array( $value )) {
|
if(is_array( $value )) {
|
||||||
$result[] = "$key:" . $this->array2json($value);
|
$result[] = "$key:" . $this->array2json($value);
|
||||||
} else {
|
} else {
|
||||||
$value = (is_bool($value)) ? $value : "\"$value\"";
|
$value = (is_bool($value)) ? $value : "\"$value\"";
|
||||||
$result[] = "$key:$value \n";
|
$result[] = "$key:$value";
|
||||||
}
|
}
|
||||||
return (isset($result)) ? "{\n".implode( ', ', $result ) ."} \n": '{}';
|
return (isset($result)) ? "{\n".implode( ', ', $result ) ."\n}\n": '{}';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -526,30 +641,30 @@ JS
|
|||||||
* @return Redirection
|
* @return Redirection
|
||||||
*/
|
*/
|
||||||
function process($data, $form) {
|
function process($data, $form) {
|
||||||
// submitted form object
|
|
||||||
$submittedForm = new SubmittedForm();
|
$submittedForm = Object::create('SubmittedForm');
|
||||||
$submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0;
|
$submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0;
|
||||||
$submittedForm->ParentID = $this->ID;
|
$submittedForm->ParentID = $this->ID;
|
||||||
$submittedForm->Recipient = $this->EmailTo;
|
|
||||||
|
// if saving is not disabled save now to generate the ID
|
||||||
if(!$this->DisableSaveSubmissions) $submittedForm->write();
|
if(!$this->DisableSaveSubmissions) $submittedForm->write();
|
||||||
|
|
||||||
// email values
|
|
||||||
$values = array();
|
$values = array();
|
||||||
$recipientAddresses = array();
|
|
||||||
$sendCopy = false;
|
|
||||||
$attachments = array();
|
$attachments = array();
|
||||||
|
|
||||||
$submittedFields = new DataObjectSet();
|
$submittedFields = new DataObjectSet();
|
||||||
|
|
||||||
foreach($this->Fields() as $field) {
|
foreach($this->Fields() as $field) {
|
||||||
// don't show fields that shouldn't be shown
|
|
||||||
if(!$field->showInReports()) continue;
|
if(!$field->showInReports()) continue;
|
||||||
|
|
||||||
|
// create a new submitted form field.
|
||||||
$submittedField = $field->getSubmittedFormField();
|
$submittedField = $field->getSubmittedFormField();
|
||||||
$submittedField->ParentID = $submittedForm->ID;
|
$submittedField->ParentID = $submittedForm->ID;
|
||||||
$submittedField->Name = $field->Name;
|
$submittedField->Name = $field->Name;
|
||||||
$submittedField->Title = $field->Title;
|
$submittedField->Title = $field->Title;
|
||||||
|
|
||||||
|
// save the value from the data
|
||||||
if($field->hasMethod('getValueFromData')) {
|
if($field->hasMethod('getValueFromData')) {
|
||||||
$submittedField->Value = $field->getValueFromData($data);
|
$submittedField->Value = $field->getValueFromData($data);
|
||||||
}
|
}
|
||||||
@ -564,34 +679,38 @@ JS
|
|||||||
// create the file from post data
|
// create the file from post data
|
||||||
$upload = new Upload();
|
$upload = new Upload();
|
||||||
$file = new File();
|
$file = new File();
|
||||||
|
|
||||||
$upload->loadIntoFile($_FILES[$field->Name], $file);
|
$upload->loadIntoFile($_FILES[$field->Name], $file);
|
||||||
|
|
||||||
// write file to form field
|
// write file to form field
|
||||||
$submittedField->UploadedFileID = $file->ID;
|
$submittedField->UploadedFileID = $file->ID;
|
||||||
|
|
||||||
// Attach the file if its less than 1MB, provide a link if its over.
|
// attach a file only if lower than 1MB
|
||||||
if($file->getAbsoluteSize() < 1024*1024*1){
|
if($file->getAbsoluteSize() < 1024*1024*1){
|
||||||
$attachments[] = $file;
|
$attachments[] = $file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$this->DisableSaveSubmissions) $submittedField->write();
|
if(!$this->DisableSaveSubmissions) $submittedField->write();
|
||||||
|
|
||||||
$submittedFields->push($submittedField);
|
$submittedFields->push($submittedField);
|
||||||
}
|
}
|
||||||
|
|
||||||
$emailData = array(
|
$emailData = array(
|
||||||
"Sender" => Member::currentUser(),
|
"Sender" => Member::currentUser(),
|
||||||
"Fields" => $submittedFields
|
"Fields" => $submittedFields
|
||||||
);
|
);
|
||||||
|
|
||||||
// email users on submit. All have their own custom options.
|
// email users on submit.
|
||||||
if($this->EmailRecipients()) {
|
if($this->EmailRecipients()) {
|
||||||
|
|
||||||
$email = new UserDefinedForm_SubmittedFormEmail($submittedFields);
|
$email = new UserDefinedForm_SubmittedFormEmail($submittedFields);
|
||||||
$email->populateTemplate($emailData);
|
$email->populateTemplate($emailData);
|
||||||
|
|
||||||
if($attachments){
|
if($attachments){
|
||||||
foreach($attachments as $file){
|
foreach($attachments as $file){
|
||||||
// bug with double decorated fields, valid ones should have an ID.
|
|
||||||
if($file->ID != 0) {
|
if($file->ID != 0) {
|
||||||
$email->attachFile($file->Filename,$file->Filename, $file->getFileType());
|
$email->attachFile($file->Filename,$file->Filename, $file->getFileType());
|
||||||
}
|
}
|
||||||
@ -638,7 +757,9 @@ JS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Director::redirect($this->Link() . 'finished?referrer=' . urlencode($data['Referrer']));
|
$referrer = (isset($data['Referrer'])) ? '?referrer=' . urlencode($data['Referrer']) : "";
|
||||||
|
|
||||||
|
return Director::redirect($this->Link() . 'finished' . $referrer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -656,7 +777,7 @@ JS
|
|||||||
array(
|
array(
|
||||||
'Link' => $referrer
|
'Link' => $referrer
|
||||||
))->renderWith('ReceivedFormSubmission'),
|
))->renderWith('ReceivedFormSubmission'),
|
||||||
'Form' => ' ',
|
'Form' => '',
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* EditableDropdown
|
* EditableDropdown
|
||||||
*
|
*
|
||||||
* Represents a modifiable dropdown box on a form
|
* Represents a modifiable dropdown (select) box on a form
|
||||||
*
|
*
|
||||||
* @package userforms
|
* @package userforms
|
||||||
*/
|
*/
|
||||||
@ -13,16 +13,20 @@ class EditableDropdown extends EditableMultipleOptionField {
|
|||||||
|
|
||||||
static $plural_name = 'Dropdowns';
|
static $plural_name = 'Dropdowns';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return DropdownField
|
||||||
|
*/
|
||||||
|
function getFormField() {
|
||||||
|
|
||||||
function getFormField($asFilter = false) {
|
|
||||||
$optionSet = $this->Options();
|
$optionSet = $this->Options();
|
||||||
$options = array();
|
$options = array();
|
||||||
|
|
||||||
if($optionSet) {
|
if($optionSet) {
|
||||||
foreach( $optionSet as $option ) {
|
foreach($optionSet as $option) {
|
||||||
$options[$option->Title] = $option->Title;
|
$options[$option->Title] = $option->Title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new DropdownField( $this->Name, $this->Title, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return new DropdownField($this->Name, $this->Title, $options);
|
||||||
|
}
|
||||||
}
|
}
|
@ -30,33 +30,38 @@ class EditableFormField extends DataObject {
|
|||||||
"Versioned('Stage', 'Live')"
|
"Versioned('Stage', 'Live')"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
protected $readonly;
|
protected $readonly;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this formfield to readonly
|
* Set the visibility of an individual form field
|
||||||
|
*
|
||||||
|
* @param bool
|
||||||
*/
|
*/
|
||||||
public function setReadonly() {
|
public function setReadonly($readonly = true) {
|
||||||
$this->readonly = true;
|
$this->readonly = $readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this field readonly to the user
|
* Returns whether this field is readonly
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isReadonly() {
|
private function isReadonly() {
|
||||||
return $this->readonly;
|
return $this->readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Template to render the form field into
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
function EditSegment() {
|
function EditSegment() {
|
||||||
return $this->renderWith('EditableFormField');
|
return $this->renderWith('EditableFormField');
|
||||||
}
|
}
|
||||||
|
|
||||||
function ClassName() {
|
|
||||||
return $this->class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether a user can delete this form field
|
* Return whether a user can delete this form field
|
||||||
* based on whether they can edit the page
|
* based on whether they can edit the page
|
||||||
@ -64,7 +69,7 @@ class EditableFormField extends DataObject {
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canDelete() {
|
public function canDelete() {
|
||||||
return $this->Parent()->canEdit();
|
return ($this->Parent()->canEdit() && !$this->isReadonly());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +79,7 @@ class EditableFormField extends DataObject {
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canEdit() {
|
public function canEdit() {
|
||||||
return $this->Parent()->canEdit();
|
return ($this->Parent()->canEdit() && !$this->isReadonly());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,6 +130,20 @@ class EditableFormField extends DataObject {
|
|||||||
$this->CustomSettings = serialize($settings);
|
$this->CustomSettings = serialize($settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a given field setting. Appends the option to the settings or overrides
|
||||||
|
* the existing value
|
||||||
|
*
|
||||||
|
* @param String key
|
||||||
|
* @param String value
|
||||||
|
*/
|
||||||
|
public function setFieldSetting($key, $value) {
|
||||||
|
$settings = $this->getFieldSettings();
|
||||||
|
$settings[$key] = $value;
|
||||||
|
|
||||||
|
$this->setFieldSettings($settings);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return just one custom setting or empty string if it does
|
* Return just one custom setting or empty string if it does
|
||||||
* not exist
|
* not exist
|
||||||
@ -147,7 +166,7 @@ class EditableFormField extends DataObject {
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function Icon() {
|
public function getIcon() {
|
||||||
return 'userforms/images/' . strtolower($this->class) . '.png';
|
return 'userforms/images/' . strtolower($this->class) . '.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,11 +236,21 @@ class EditableFormField extends DataObject {
|
|||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Title field of the field in the backend of the page
|
||||||
|
*
|
||||||
|
* @return TextField
|
||||||
|
*/
|
||||||
function TitleField() {
|
function TitleField() {
|
||||||
$titleAttr = Convert::raw2att($this->Title);
|
$titleAttr = Convert::raw2att($this->Title);
|
||||||
$readOnlyAttr = (!$this->canEdit()) ? ' disabled="disabled"' : '';
|
$readOnlyAttr = (!$this->canEdit()) ? ' disabled="disabled"' : '';
|
||||||
|
|
||||||
return "<input type=\"text\" class=\"text\" title=\"("._t('EditableFormField.ENTERQUESTION', 'Enter Question').")\" value=\"$titleAttr\" name=\"Fields[{$this->ID}][Title]\"$readOnlyAttr />";
|
$field = new TextField('Title', _t('EditableFormField.ENTERQUESTION', 'Enter Question'), $this->Title);
|
||||||
|
$field->setName($this->getFieldName('Title'));
|
||||||
|
|
||||||
|
if(!$this->canEdit()) $field->setReadonly(true);
|
||||||
|
|
||||||
|
return $field;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,6 +35,9 @@ class EditableTextField extends EditableFormField {
|
|||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return TextareaField|TextField
|
||||||
|
*/
|
||||||
function getFormField() {
|
function getFormField() {
|
||||||
if($this->getSetting('Rows') && $this->getSetting('Rows') > 1) {
|
if($this->getSetting('Rows') && $this->getSetting('Rows') > 1) {
|
||||||
return new TextareaField($this->Name, $this->Title, $this->getSetting('Rows'));
|
return new TextareaField($this->Name, $this->Title, $this->getSetting('Rows'));
|
||||||
|
@ -1,36 +1,51 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* Allows CMS user to create forms dynamically.
|
* A form field allowing a user to customize and create form fields.
|
||||||
|
* for saving into a {@link UserDefinedForm}
|
||||||
*
|
*
|
||||||
* @package userforms
|
* @package userforms
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class FieldEditor extends FormField {
|
class FieldEditor extends FormField {
|
||||||
|
|
||||||
protected $hasFormOptions = true;
|
/**
|
||||||
|
* Field Editor Template
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
function FieldHolder() {
|
function FieldHolder() {
|
||||||
return $this->renderWith("FieldEditor");
|
return $this->renderWith("FieldEditor");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can a user edit this field?
|
* Returns whether a user can edit the form
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function canEdit() {
|
public function canEdit() {
|
||||||
if($this->readonly) return false;
|
if($this->readonly) return false;
|
||||||
|
|
||||||
return $this->form->getRecord()->canEdit();
|
return $this->form->getRecord()->canEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can a user delete this field?
|
* Returns whether a user delete a field in the form. The {@link EditableFormField}s
|
||||||
|
* check if they can delete themselves but this counts as an {@link self::canEdit()}
|
||||||
|
* function rather than a delete
|
||||||
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function canDelete() {
|
public function canDelete() {
|
||||||
if($this->readonly) return false;
|
if($this->readonly) return false;
|
||||||
return $this->form->getRecord()->canDelete();
|
|
||||||
|
return $this->form->getRecord()->canEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform this form field to a readonly version.
|
||||||
|
*
|
||||||
|
* @return ViewableData_Customised
|
||||||
|
*/
|
||||||
function performReadonlyTransformation() {
|
function performReadonlyTransformation() {
|
||||||
$clone = clone $this;
|
$clone = clone $this;
|
||||||
$clone->readonly = true;
|
$clone->readonly = true;
|
||||||
@ -43,15 +58,11 @@ class FieldEditor extends FormField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the Form Fields for the user forms
|
* Return the fields for the user forms
|
||||||
*
|
*
|
||||||
* @return DataObjectSet
|
* @return DataObjectSet
|
||||||
*/
|
*/
|
||||||
function Fields() {
|
function Fields() {
|
||||||
Requirements::css("userforms/css/FieldEditor.css");
|
|
||||||
Requirements::javascript(SAPPHIRE_DIR ."/thirdparty/jquery-ui/jquery-ui-1.8rc3.custom.js");
|
|
||||||
Requirements::javascript("userforms/javascript/UserForm.js");
|
|
||||||
|
|
||||||
// Don't return any fields unless we actually have the dependent parameters set on the form field
|
// Don't return any fields unless we actually have the dependent parameters set on the form field
|
||||||
if($this->form && $this->form->getRecord() && $this->name) {
|
if($this->form && $this->form->getRecord() && $this->name) {
|
||||||
$relationName = $this->name;
|
$relationName = $this->name;
|
||||||
@ -67,6 +78,7 @@ class FieldEditor extends FormField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,10 +173,6 @@ class FieldEditor extends FormField {
|
|||||||
if($record->hasMethod('customFormSave')) {
|
if($record->hasMethod('customFormSave')) {
|
||||||
$record->customFormSave($_REQUEST[$name], $record);
|
$record->customFormSave($_REQUEST[$name], $record);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($record->hasMethod('processNewFormFields')) {
|
|
||||||
$record->processNewFormFields();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,28 +238,4 @@ class FieldEditor extends FormField {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setHasFormOptions($bool){
|
|
||||||
$this->hasFormOptions = $bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasFormOptions(){
|
|
||||||
return $this->hasFormOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
function FormOptions() {
|
|
||||||
if($this->hasFormOptions()){
|
|
||||||
if($this->form->getRecord()->hasMethod('customFormActions')) {
|
|
||||||
$newFields = $this->form->getRecord()->customFormActions($this->readonly);
|
|
||||||
|
|
||||||
foreach($newFields as $newField) {
|
|
||||||
$newField->setName("{$this->name}[{$newField->Name()}]" );
|
|
||||||
}
|
|
||||||
if($this->readonly) {
|
|
||||||
$newFields = $newFields->makeReadonly();
|
|
||||||
}
|
|
||||||
return $newFields;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class SubmittedForm extends DataObject {
|
class SubmittedForm extends DataObject {
|
||||||
|
|
||||||
static $has_one = array(
|
static $has_one = array(
|
||||||
"SubmittedBy" => "Member",
|
"SubmittedBy" => "Member",
|
||||||
"Parent" => "UserDefinedForm",
|
"Parent" => "UserDefinedForm",
|
||||||
|
@ -69,93 +69,7 @@ class SubmittedFormReportField extends FormField {
|
|||||||
// Get the UserDefinedForm to export data from the URL
|
// Get the UserDefinedForm to export data from the URL
|
||||||
$SQL_ID = (isset($_REQUEST['id'])) ? Convert::raw2sql($_REQUEST['id']) : false;
|
$SQL_ID = (isset($_REQUEST['id'])) ? Convert::raw2sql($_REQUEST['id']) : false;
|
||||||
|
|
||||||
if($SQL_ID) {
|
return $this->generateExport($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\"");
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(class_exists('SS_HTTPRequest')) {
|
|
||||||
SS_HTTPRequest::send_file($csvData, $fileName)->output();
|
|
||||||
} else {
|
|
||||||
HTTPRequest::send_file($csvData, $fileName)->output();
|
|
||||||
}
|
|
||||||
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
user_error("'$SQL_ID' is not a valid UserDefinedForm ID.", E_USER_ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
(function($) {
|
(function($) {
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the Field sortable
|
||||||
|
*/
|
||||||
|
update_sortable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the sortable properties of the form as a function
|
* Update the sortable properties of the form as a function
|
||||||
@ -75,11 +79,6 @@
|
|||||||
|
|
||||||
/*-------------------- FIELD EDITOR ----------------------- */
|
/*-------------------- FIELD EDITOR ----------------------- */
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the Field sortable
|
|
||||||
*/
|
|
||||||
update_sortable();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new instance of a field in the current form
|
* Create a new instance of a field in the current form
|
||||||
* area. the type information should all be on this object
|
* area. the type information should all be on this object
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
<!-- JS Relys on EditableFormField as a class - and the 3 ids in this order - do not change -->
|
<!-- JS Relys on EditableFormField as a class - and the 3 ids in this order - do not change -->
|
||||||
<li class="$ClassName EditableFormField" id="$Name.ATT EditableItem_$Pos $Name">
|
<li class="$ClassName EditableFormField" id="$Name.ATT EditableItem_$Pos $Name">
|
||||||
<div class="fieldInfo">
|
<div class="fieldInfo">
|
||||||
<% if isReadonly %>
|
<% if canEdit %>
|
||||||
<img class="fieldHandler" src="sapphire/images/drag_readonly.gif" alt="<% _t('LOCKED', 'These fields cannot be modified') %>" />
|
|
||||||
<% else %>
|
|
||||||
<img class="fieldHandler" src="sapphire/images/drag.gif" alt="<% _t('DRAG', 'Drag to rearrange order of fields') %>" />
|
<img class="fieldHandler" src="sapphire/images/drag.gif" alt="<% _t('DRAG', 'Drag to rearrange order of fields') %>" />
|
||||||
|
<% else %>
|
||||||
|
<img class="fieldHandler" src="sapphire/images/drag_readonly.gif" alt="<% _t('LOCKED', 'These fields cannot be modified') %>" />
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<img class="icon" src="$Icon" alt="$ClassName" title="$singular_name" />
|
<img class="icon" src="$Icon" alt="$ClassName" title="$singular_name" />
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
<input type="text" name="{$FieldName}[Title]" value="$Title" />
|
<input type="text" name="{$FieldName}[Title]" value="$Title" />
|
||||||
<input type="hidden" class="sortOptionHidden hidden" name="{$FieldName}[Sort]" value="$Sort" />
|
<input type="hidden" class="sortOptionHidden hidden" name="{$FieldName}[Sort]" value="$Sort" />
|
||||||
|
|
||||||
<% if isReadonly %>
|
<% if canEdit %>
|
||||||
<img src="cms/images/locked.gif" alt="<% _t('LOCKED', 'These fields cannot be modified') %>" />
|
|
||||||
<% else %>
|
|
||||||
<a href="$ID" class="deleteOption"><img src="cms/images/delete.gif" alt="<% _t('DELETE', 'Remove this option') %>" /></a>
|
<a href="$ID" class="deleteOption"><img src="cms/images/delete.gif" alt="<% _t('DELETE', 'Remove this option') %>" /></a>
|
||||||
|
<% else %>
|
||||||
|
<img src="cms/images/locked.gif" alt="<% _t('LOCKED', 'These fields cannot be modified') %>" />
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</li>
|
</li>
|
@ -1,4 +1,8 @@
|
|||||||
<div class="FieldEditor <% if isReadonly %>readonly<% end_if %>" id="Fields">
|
<% require css(userforms/css/FieldEditor.css) %>
|
||||||
|
<% require javascript(sapphire/thirdparty/jquery-ui/jquery-ui-1.8rc3.custom.js) %>
|
||||||
|
<% require javascript(userforms/javascript/UserForm.js) %>
|
||||||
|
|
||||||
|
<div class="FieldEditor <% if canEdit %><% else %>readonly<% end_if %>" id="Fields">
|
||||||
|
|
||||||
<div class="FieldListHold">
|
<div class="FieldListHold">
|
||||||
<ul class="FieldList" id="Fields_fields">
|
<ul class="FieldList" id="Fields_fields">
|
||||||
@ -8,12 +12,19 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% include AddField %>
|
<% if canEdit %>
|
||||||
|
<div class="MenuHolder">
|
||||||
|
<h2><% _t('ADD', 'Add') %></h2>
|
||||||
|
|
||||||
<div class="FormOptions">
|
<select name="AddUserFormField" id="AddUserFormField">
|
||||||
<h3>Form Options</h3>
|
<option value=""><% _t('SELECTAFIELD', 'Select a Field') %></option>
|
||||||
<% control FormOptions %>
|
|
||||||
$FieldHolder
|
<% control CreatableFields %>
|
||||||
|
<option value="$ClassName">$Title</option>
|
||||||
<% end_control %>
|
<% end_control %>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input type="submit" class="action" value="<% _t('ADD', 'Add') %>" />
|
||||||
</div>
|
</div>
|
||||||
|
<% end_if %>
|
||||||
</div>
|
</div>
|
@ -1,14 +0,0 @@
|
|||||||
<% if canEdit %>
|
|
||||||
<div class="MenuHolder">
|
|
||||||
<h2><% _t('ADD', 'Add') %></h2>
|
|
||||||
<select name="AddUserFormField" id="AddUserFormField">
|
|
||||||
<option value=""><% _t('SELECTAFIELD', 'Select a Field') %></option>
|
|
||||||
|
|
||||||
<% control CreatableFields %>
|
|
||||||
<option value="$ClassName">$Title</option>
|
|
||||||
<% end_control %>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<input type="submit" class="action" value="<% _t('ADD', 'Add') %>" />
|
|
||||||
</div>
|
|
||||||
<% end_if %>
|
|
45
tests/EditableFormFieldTest.php
Normal file
45
tests/EditableFormFieldTest.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package userforms
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EditableFormFieldTest extends FunctionalTest {
|
||||||
|
|
||||||
|
static $fixture_file = 'userforms/tests/EditableFormFields.yml';
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
|
||||||
|
$this->assertThat($field, $this->isInstanceOf('DropdownField'));
|
||||||
|
$values = $field->getSource();
|
||||||
|
|
||||||
|
$this->assertEquals(array('Option 1' => 'Option 1', 'Option 2' => 'Option 2'), $values);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
62
tests/EditableFormFields.yml
Normal file
62
tests/EditableFormFields.yml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
EditableTextField:
|
||||||
|
basic-text:
|
||||||
|
Name: basic-text-name
|
||||||
|
Title: Basic Text Field
|
||||||
|
|
||||||
|
required-text:
|
||||||
|
Name: required-text-field
|
||||||
|
Title: Required Text Field
|
||||||
|
CustomErrorMessage: Custom Error Message
|
||||||
|
RightTitle: Right Title
|
||||||
|
Required: true
|
||||||
|
|
||||||
|
EditableRadioField:
|
||||||
|
radio-field:
|
||||||
|
Name: radio-option
|
||||||
|
Title: Radio Option
|
||||||
|
|
||||||
|
EditableDropdown:
|
||||||
|
basic-dropdown:
|
||||||
|
Name: basic-dropdown
|
||||||
|
Title: Basic Dropdown Field
|
||||||
|
Options: =>EditableOption.option-1, =>EditableOption.option-2
|
||||||
|
|
||||||
|
department-dropdown:
|
||||||
|
Name: department
|
||||||
|
Title: Department
|
||||||
|
Options: =>EditableOption.department-1, =>EditableOption.department-2
|
||||||
|
|
||||||
|
EditableCheckbox:
|
||||||
|
checkbox-1:
|
||||||
|
Name: checkbox-1
|
||||||
|
Title: Checkbox 1
|
||||||
|
|
||||||
|
EditableEmailField:
|
||||||
|
email-field:
|
||||||
|
Name: email-field
|
||||||
|
Title: Email
|
||||||
|
|
||||||
|
EditableCheckboxGroupField:
|
||||||
|
checkbox-group:
|
||||||
|
Name: check-box-group
|
||||||
|
Title: Check box group
|
||||||
|
Options: =>EditableOption.option-1, =>EditableOption.option-2
|
37
tests/FieldEditorTest.php
Normal file
37
tests/FieldEditorTest.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests covering the form editor / builder and
|
||||||
|
* some of the user interface
|
||||||
|
*
|
||||||
|
* @package userforms
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FieldEditorTest extends FunctionalTest {
|
||||||
|
|
||||||
|
static $fixture_file = 'userforms/tests/UserDefinedFormTest.yml';
|
||||||
|
|
||||||
|
function testPerformReadonlyTransformation() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testSaveInto() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAddField() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testAddOptionField() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCreatableFields() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FieldEditorTest_Controller extends Controller {
|
||||||
|
|
||||||
|
}
|
68
tests/SubmittedFormTest.php
Normal file
68
tests/SubmittedFormTest.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class SubmittedFormTest extends FunctionalTest {
|
||||||
|
|
||||||
|
static $fixture_file = 'userforms/tests/SubmittedFormTest.yml';
|
||||||
|
|
||||||
|
function testReportSubmissions() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testCSVExport() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testdeletesubmission() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testdeletesubmissions() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testOnBeforeDeleteOfForm() {
|
||||||
|
$field = $this->objFromFixture('SubmittedFormField', 'submitted-form-field-1');
|
||||||
|
$form = $field->Parent();
|
||||||
|
|
||||||
|
$this->assertEquals($form->FieldValues()->Count(), 2);
|
||||||
|
$form->delete();
|
||||||
|
|
||||||
|
$fields = DataObject::get('SubmittedFormField', "ParentID = '$form->ID'");
|
||||||
|
|
||||||
|
$this->assertNull($fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGetFormattedValue() {
|
||||||
|
$field = $this->objFromFixture('SubmittedFormField', 'submitted-form-field-1');
|
||||||
|
|
||||||
|
$this->assertEquals('1', $field->getFormattedValue());
|
||||||
|
|
||||||
|
$textarea = $this->objFromFixture('SubmittedFormField', 'submitted-textarea-1');
|
||||||
|
|
||||||
|
$text = "I am here testing<br />\nTesting until I cannot<br />\nI love my testing";
|
||||||
|
|
||||||
|
$this->assertEquals($text, $textarea->getFormattedValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFileGetLink() {
|
||||||
|
$field = $this->objFromFixture('SubmittedFileField', 'submitted-file-1');
|
||||||
|
|
||||||
|
// @todo add checks for if no file can be downloaded
|
||||||
|
$this->assertContains('my-file.jpg', $field->getLink());
|
||||||
|
|
||||||
|
}
|
||||||
|
function testFileGetFormattedValue() {
|
||||||
|
$field = $this->objFromFixture('SubmittedFileField', 'submitted-file-1');
|
||||||
|
|
||||||
|
// @todo add checks for if no file can be downloaded
|
||||||
|
$this->assertContains('Download File', $field->getFormattedValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SubmittedFormTest_Controller extends Controller {
|
||||||
|
|
||||||
|
function ReportField() {
|
||||||
|
return new Form($this, 'ReportField', new FieldSet(new SubmittedFormReportField('Report'), new FieldSet()));
|
||||||
|
}
|
||||||
|
}
|
43
tests/SubmittedFormTest.yml
Normal file
43
tests/SubmittedFormTest.yml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
UserDefinedForm:
|
||||||
|
form-page:
|
||||||
|
Title: Form
|
||||||
|
|
||||||
|
form-page-2:
|
||||||
|
Title: Second Form
|
||||||
|
|
||||||
|
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
|
||||||
|
Name: field-1
|
||||||
|
Title: Field 1
|
||||||
|
Value: 1
|
||||||
|
|
||||||
|
submitted-textarea-1:
|
||||||
|
Parent: =>SubmittedForm.submitted-form-2
|
||||||
|
Name: field 2
|
||||||
|
Title: Field 2
|
||||||
|
Value: |
|
||||||
|
I am here testing
|
||||||
|
Testing until I cannot
|
||||||
|
I love my testing
|
||||||
|
|
||||||
|
SubmittedFileField:
|
||||||
|
submitted-file-1:
|
||||||
|
Name: File Field
|
||||||
|
Title: File
|
||||||
|
Parent: =>SubmittedForm.submitted-form-1
|
||||||
|
UploadedFile: =>File.uploaded-file
|
||||||
|
|
||||||
|
|
196
tests/UserDefinedFormControllerTest.php
Normal file
196
tests/UserDefinedFormControllerTest.php
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package userforms
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UserDefinedFormControllerTest extends FunctionalTest {
|
||||||
|
|
||||||
|
static $fixture_file = 'userforms/tests/UserDefinedFormTest.yml';
|
||||||
|
|
||||||
|
function testProcess() {
|
||||||
|
$form = $this->setupFormFrontend();
|
||||||
|
|
||||||
|
$controller = new UserDefinedForm_Controller($form);
|
||||||
|
|
||||||
|
$this->autoFollowRedirection = false;
|
||||||
|
$this->clearEmails();
|
||||||
|
|
||||||
|
// load the form
|
||||||
|
$this->get($form->URLSegment);
|
||||||
|
$response = $this->submitForm('Form_Form', null, array('basic-text-name' => 'Basic Value'));
|
||||||
|
|
||||||
|
// should have a submitted form field now
|
||||||
|
$submitted = DataObject::get('SubmittedFormField', "\"Name\" = 'basic-text-name'");
|
||||||
|
$this->assertDOSAllMatch(array('Name' => 'basic-text-name', 'Value' => 'Basic Value', 'Title' => 'Basic Text Field'), $submitted);
|
||||||
|
|
||||||
|
// check emails
|
||||||
|
$this->assertEmailSent('test@example.com', 'no-reply@example.com', 'Email Subject');
|
||||||
|
$email = $this->findEmail('test@example.com', 'no-reply@example.com', 'Email Subject');
|
||||||
|
|
||||||
|
// assert that the email has the field title and the value html email
|
||||||
|
$parser = new CSSContentParser($email['content']);
|
||||||
|
$title = $parser->getBySelector('strong');
|
||||||
|
|
||||||
|
$this->assertEquals('Basic Text Field', (string) $title[0], 'Email contains the field name');
|
||||||
|
|
||||||
|
$value = $parser->getBySelector('dd');
|
||||||
|
$this->assertEquals('Basic Value', (string) $value[0], 'Email contains the value');
|
||||||
|
|
||||||
|
// no html
|
||||||
|
$this->assertEmailSent('nohtml@example.com', 'no-reply@example.com', 'Email Subject');
|
||||||
|
$nohtml = $this->findEmail('nohtml@example.com', 'no-reply@example.com', 'Email Subject');
|
||||||
|
|
||||||
|
$this->assertContains('Basic Text Field - Basic Value', $nohtml['content'], 'Email contains no html');
|
||||||
|
|
||||||
|
// no data
|
||||||
|
$this->assertEmailSent('nodata@example.com', 'no-reply@example.com', 'Email Subject');
|
||||||
|
$nodata = $this->findEmail('nodata@example.com', 'no-reply@example.com', 'Email Subject');
|
||||||
|
|
||||||
|
$parser = new CSSContentParser($nodata['content']);
|
||||||
|
$list = $parser->getBySelector('dl');
|
||||||
|
|
||||||
|
$this->assertFalse(isset($list[0]), 'Email contains no fields');
|
||||||
|
|
||||||
|
// check to see if the user was redirected (301)
|
||||||
|
$this->assertEquals($response->getStatusCode(), 302);
|
||||||
|
$this->assertStringEndsWith('finished', $response->getHeader('Location'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFinished() {
|
||||||
|
$form = $this->setupFormFrontend();
|
||||||
|
$response = $this->get($form->URLSegment.'/finished');
|
||||||
|
|
||||||
|
$this->assertContains($form->OnCompleteMessage ,$response->getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
function testForm() {
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$controller = new UserDefinedForm_Controller($form);
|
||||||
|
|
||||||
|
// test form
|
||||||
|
$this->assertEquals($controller->Form()->Name(), 'Form', 'The form is referenced as Form');
|
||||||
|
|
||||||
|
$this->assertEquals($controller->Form()->Fields()->Count(), 2);
|
||||||
|
$this->assertEquals($controller->Form()->Actions()->Count(), 1);
|
||||||
|
$this->assertEquals(count($controller->Form()->getValidator()->getRequired()), 0);
|
||||||
|
|
||||||
|
$requiredForm = $this->objFromFixture('UserDefinedForm', 'validation-form');
|
||||||
|
$controller = new UserDefinedForm_Controller($requiredForm);
|
||||||
|
|
||||||
|
$this->assertEquals($controller->Form()->Fields()->Count(), 2);
|
||||||
|
$this->assertEquals($controller->Form()->Actions()->Count(), 1);
|
||||||
|
$this->assertEquals(count($controller->Form()->getValidator()->getRequired()), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testgetFormFields() {
|
||||||
|
// generating the fieldset of fields
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$controller = new UserDefinedForm_Controller($form);
|
||||||
|
|
||||||
|
$fields = $controller->getFormFields();
|
||||||
|
|
||||||
|
$this->assertEquals($fields->Count(), 1);
|
||||||
|
|
||||||
|
// custom error message on a form field
|
||||||
|
$requiredForm = $this->objFromFixture('UserDefinedForm', 'validation-form');
|
||||||
|
$controller = new UserDefinedForm_Controller($requiredForm);
|
||||||
|
|
||||||
|
UserDefinedForm::$required_identifier = "*";
|
||||||
|
|
||||||
|
$fields = $controller->getFormFields();
|
||||||
|
|
||||||
|
$this->assertEquals($fields->First()->getCustomValidationMessage(), 'Custom Error Message');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGetFormActions() {
|
||||||
|
// generating the fieldset of actions
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$controller = new UserDefinedForm_Controller($form);
|
||||||
|
$actions = $controller->getFormActions();
|
||||||
|
|
||||||
|
// by default will have 1 submit button which links to process
|
||||||
|
$expected = new FieldSet(new FormAction('process', 'Submit'));
|
||||||
|
|
||||||
|
$this->assertEquals($actions, $expected);
|
||||||
|
|
||||||
|
// the custom popup should have a reset button and a custom text
|
||||||
|
$custom = $this->objFromFixture('UserDefinedForm', 'form-with-reset-and-custom-action');
|
||||||
|
$controller = new UserDefinedForm_Controller($custom);
|
||||||
|
|
||||||
|
$actions = $controller->getFormActions();
|
||||||
|
|
||||||
|
$expected = new FieldSet(new FormAction('process', 'Custom Button'));
|
||||||
|
$expected->push(new ResetFormAction("clearForm"));
|
||||||
|
|
||||||
|
$this->assertEquals($actions, $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testArrayToJson() {
|
||||||
|
$array = array('1' => 'one', '2' => 'two');
|
||||||
|
$string = "{\n1:\"one\", 2:\"two\"\n}\n";
|
||||||
|
$this->assertEquals(UserDefinedForm_Controller::array2json($array), $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function testRenderingIntoFormTemplate() {
|
||||||
|
$form = $this->setupFormFrontend();
|
||||||
|
|
||||||
|
$form->Content = 'This is some content without a form nested between it';
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
$controller = new UserDefinedForm_Controller($form);
|
||||||
|
|
||||||
|
// check to see if $Form is replaced to inside the content
|
||||||
|
$index = new ArrayData($controller->index());
|
||||||
|
$parser = new CSSContentParser($index->renderWith(array('UserDefinedFormControllerTest')));
|
||||||
|
|
||||||
|
$this->checkTemplateIsCorrect($parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testRenderingIntoTemplateWithSubstringReplacement() {
|
||||||
|
$form = $this->setupFormFrontend();
|
||||||
|
|
||||||
|
$controller = new UserDefinedForm_Controller($form);
|
||||||
|
|
||||||
|
// check to see if $Form is replaced to inside the content
|
||||||
|
$index = new ArrayData($controller->index());
|
||||||
|
$parser = new CSSContentParser($index->renderWith(array('UserDefinedFormControllerTest')));
|
||||||
|
|
||||||
|
$this->checkTemplateIsCorrect($parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupFormFrontend() {
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
$member = Member::currentUser();
|
||||||
|
$member->logOut();
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTemplateIsCorrect($parser) {
|
||||||
|
$this->assertArrayHasKey(0, $parser->getBySelector('form#Form_Form'));
|
||||||
|
|
||||||
|
// check for the input
|
||||||
|
$this->assertArrayHasKey(0, $parser->getBySelector('input.text'));
|
||||||
|
|
||||||
|
// check for the label and the text
|
||||||
|
$label = $parser->getBySelector('label.left');
|
||||||
|
$this->assertArrayHasKey(0, $label);
|
||||||
|
|
||||||
|
$this->assertEquals((string) $label[0][0], "Basic Text Field", "Label contains correct field name");
|
||||||
|
|
||||||
|
// check for the action
|
||||||
|
$action = $parser->getBySelector('input.action');
|
||||||
|
$this->assertArrayHasKey(0, $action);
|
||||||
|
|
||||||
|
$this->assertEquals((string) $action[0]['value'], "Submit", "Submit button has default text");
|
||||||
|
}
|
||||||
|
}
|
@ -1,102 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests covering the form editor / builder and
|
|
||||||
* some of the user interface
|
|
||||||
*
|
|
||||||
* @package userforms
|
|
||||||
*/
|
|
||||||
|
|
||||||
class UserDefinedFormEditorTest extends FunctionalTest {
|
|
||||||
|
|
||||||
protected $form;
|
|
||||||
|
|
||||||
function setUp() {
|
|
||||||
parent::setUp();
|
|
||||||
$this->logInWithPermission('ADMIN');
|
|
||||||
|
|
||||||
$this->form = new UserDefinedForm();
|
|
||||||
$this->form->write();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPublishingNormalField() {
|
|
||||||
$id = $this->form->ID;
|
|
||||||
|
|
||||||
// test a normal field
|
|
||||||
$field = new EditableFormField();
|
|
||||||
$field->write();
|
|
||||||
|
|
||||||
$this->form->Fields()->add($field);
|
|
||||||
|
|
||||||
// upon adding it, it shouldn't be on the live site
|
|
||||||
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $id");
|
|
||||||
$this->assertFalse($live);
|
|
||||||
|
|
||||||
// upon publishing the field should exist
|
|
||||||
$this->form->doPublish();
|
|
||||||
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $id");
|
|
||||||
$this->assertEquals($live->Fields()->Count(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testPublishingMultipleOptions() {
|
|
||||||
$id = $this->form->ID;
|
|
||||||
$this->form->Fields()->removeAll();
|
|
||||||
|
|
||||||
// test a editable option field
|
|
||||||
$dropdown = new EditableDropdown();
|
|
||||||
$dropdown->write();
|
|
||||||
|
|
||||||
$checkbox = new EditableCheckboxGroupField();
|
|
||||||
$checkbox->write();
|
|
||||||
|
|
||||||
$option = new EditableOption();
|
|
||||||
$option->write();
|
|
||||||
|
|
||||||
$option2 = new EditableOption();
|
|
||||||
$option2->write();
|
|
||||||
|
|
||||||
$dropdown->Options()->add($option);
|
|
||||||
$checkbox->Options()->add($option2);
|
|
||||||
|
|
||||||
$this->form->Fields()->add($dropdown);
|
|
||||||
$this->form->Fields()->add($checkbox);
|
|
||||||
|
|
||||||
// upon adding it, it shouldn't be on the live site
|
|
||||||
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $id");
|
|
||||||
$this->assertFalse($live);
|
|
||||||
|
|
||||||
// and when published it should exist and the option
|
|
||||||
$this->form->doPublish();
|
|
||||||
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $id");
|
|
||||||
$this->assertEquals($live->Fields()->Count(), 2);
|
|
||||||
|
|
||||||
// check they have options attached
|
|
||||||
foreach($live->Fields() as $field) {
|
|
||||||
$this->assertEquals($field->Options()->Count(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testUnpublishing() {
|
|
||||||
$id = $this->form->ID;
|
|
||||||
$this->form->Fields()->removeAll();
|
|
||||||
$this->form->Fields()->add(new EditableFormField());
|
|
||||||
$this->form->doUnPublish();
|
|
||||||
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $id");
|
|
||||||
$stage = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $id");
|
|
||||||
$this->assertEquals($live, false);
|
|
||||||
$this->assertEquals($stage->Fields()->Count(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function testDuplicatingPage() {
|
|
||||||
$this->form->Fields()->add(new EditableFormField());
|
|
||||||
$form_copy = $this->form->duplicate();
|
|
||||||
|
|
||||||
$this->assertEquals($this->form->Fields()->Count(), $form_copy->Fields()->Count());
|
|
||||||
}
|
|
||||||
|
|
||||||
function tearDown() {
|
|
||||||
$this->form->delete();
|
|
||||||
|
|
||||||
parent::tearDown();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Various tests for the user defined forms.
|
|
||||||
* Does not test the user interface in the admin.
|
|
||||||
*
|
|
||||||
* @todo Add more comprehensive tests
|
|
||||||
* @package userforms
|
|
||||||
*/
|
|
||||||
|
|
||||||
class UserDefinedFormFieldTest extends SapphireTest {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Basic Test creating all the editable form fields
|
|
||||||
*/
|
|
||||||
function testCreatingAllFields() {
|
|
||||||
$fields = ClassInfo::subclassesFor('EditableFormField');
|
|
||||||
foreach($fields as $field) {
|
|
||||||
$object = new $field();
|
|
||||||
$object->Name = "$field";
|
|
||||||
$object->Title = "$field";
|
|
||||||
$object->write();
|
|
||||||
|
|
||||||
$this->assertEquals($field, $object->Name);
|
|
||||||
$object->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
221
tests/UserDefinedFormTest.php
Normal file
221
tests/UserDefinedFormTest.php
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @package userforms
|
||||||
|
*/
|
||||||
|
|
||||||
|
class UserDefinedFormTest extends FunctionalTest {
|
||||||
|
|
||||||
|
static $fixture_file = 'userforms/tests/UserDefinedFormTest.yml';
|
||||||
|
|
||||||
|
|
||||||
|
function testRollbackToVersion() {
|
||||||
|
// @todo rolling back functionality (eg fields) is not supported yet
|
||||||
|
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$form->SubmitButtonText = 'Button Text';
|
||||||
|
$form->write();
|
||||||
|
$form->doPublish();
|
||||||
|
$origVersion = $form->Version;
|
||||||
|
|
||||||
|
$form->SubmitButtonText = 'Updated Button Text';
|
||||||
|
$form->write();
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
// check published site
|
||||||
|
$updated = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
|
||||||
|
$this->assertEquals($updated->SubmitButtonText, 'Updated Button Text');
|
||||||
|
|
||||||
|
$form->doRollbackTo($origVersion);
|
||||||
|
|
||||||
|
$orignal = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
|
||||||
|
$this->assertEquals($orignal->SubmitButtonText, 'Button Text');
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGetCMSFields() {
|
||||||
|
// ensure all the tabs are present.
|
||||||
|
// @todo a common bug with this is translations messing up the tabs.
|
||||||
|
// @todo only logic we should check for is that the tablelistfield filter
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$fields = $form->getCMSFields();
|
||||||
|
|
||||||
|
$this->assertTrue($fields->dataFieldByName('Fields') !== null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('EmailRecipients') != null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('Reports') != null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('OnCompleteMessage') != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testEmailRecipientPopup() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$popup = new UserDefinedForm_EmailRecipient();
|
||||||
|
|
||||||
|
$fields = $popup->getCMSFields_forPopup();
|
||||||
|
|
||||||
|
$this->assertTrue($fields->dataFieldByName('EmailSubject') !== null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('EmailFrom') !== null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('EmailAddress') !== null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('HideFormData') !== null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('SendPlain') !== null);
|
||||||
|
$this->assertTrue($fields->dataFieldByName('EmailBody') !== null);
|
||||||
|
|
||||||
|
// add an email field, it should now add a or from X address picker
|
||||||
|
$email = $this->objFromFixture('EditableEmailField','email-field');
|
||||||
|
$form->Fields()->add($email);
|
||||||
|
|
||||||
|
$popup->Form = $form;
|
||||||
|
$popup->write();
|
||||||
|
|
||||||
|
$fields = $popup->getCMSFields_forPopup();
|
||||||
|
$this->assertThat($fields->fieldByName('SendEmailToFieldID'), $this->isInstanceOf('DropdownField'));
|
||||||
|
|
||||||
|
// if the front end has checkboxs or dropdown they can select from that can also be used to send things
|
||||||
|
$dropdown = $this->objFromFixture('EditableDropdown', 'department-dropdown');
|
||||||
|
$form->Fields()->add($dropdown);
|
||||||
|
|
||||||
|
$fields = $popup->getCMSFields_forPopup();
|
||||||
|
$this->assertTrue($fields->dataFieldByName('SendEmailToFieldID') !== null);
|
||||||
|
|
||||||
|
$popup->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
function testPublishing() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
$form->write();
|
||||||
|
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
|
||||||
|
|
||||||
|
$this->assertNotNull($live);
|
||||||
|
$this->assertEquals($live->Fields()->Count(), 1);
|
||||||
|
|
||||||
|
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown');
|
||||||
|
$form->Fields()->add($dropdown);
|
||||||
|
|
||||||
|
$stage = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
|
||||||
|
$this->assertEquals($stage->Fields()->Count(), 2);
|
||||||
|
|
||||||
|
// should not have published the dropdown
|
||||||
|
$liveDropdown = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $dropdown->ID");
|
||||||
|
$this->assertFalse($liveDropdown);
|
||||||
|
|
||||||
|
// when publishing it should have added it
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
|
||||||
|
$this->assertEquals($live->Fields()->Count(), 2);
|
||||||
|
|
||||||
|
// edit the title
|
||||||
|
$text = $form->Fields()->First();
|
||||||
|
|
||||||
|
$text->Title = 'Edited title';
|
||||||
|
$text->write();
|
||||||
|
|
||||||
|
$liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID");
|
||||||
|
$this->assertFalse($liveText->Title == $text->Title);
|
||||||
|
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
$liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID");
|
||||||
|
$this->assertTrue($liveText->Title == $text->Title);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testUnpublishing() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
$form->write();
|
||||||
|
|
||||||
|
$form->doPublish();
|
||||||
|
|
||||||
|
// assert that it exists and has a field
|
||||||
|
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
|
||||||
|
|
||||||
|
$this->assertTrue(isset($live));
|
||||||
|
$this->assertEquals(DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value(), 1);
|
||||||
|
|
||||||
|
// unpublish
|
||||||
|
$form->doUnpublish();
|
||||||
|
|
||||||
|
$this->assertFalse(Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID"));
|
||||||
|
$this->assertEquals(DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value(), 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDoRevertToLive() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
$form->SubmitButtonText = 'Button Text';
|
||||||
|
$form->doPublish();
|
||||||
|
$text = $form->Fields()->First();
|
||||||
|
|
||||||
|
$form->SubmitButtonText = 'Edited Button Text';
|
||||||
|
$form->write();
|
||||||
|
|
||||||
|
$text->Title = 'Edited title';
|
||||||
|
$text->write();
|
||||||
|
|
||||||
|
// check that the published version is not updated
|
||||||
|
$liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID");
|
||||||
|
|
||||||
|
$revertTo = $liveText->Title;
|
||||||
|
|
||||||
|
$this->assertFalse($revertTo == $text->Title);
|
||||||
|
|
||||||
|
// revert back to the live data
|
||||||
|
$form->doRevertToLive();
|
||||||
|
|
||||||
|
$check = Versioned::get_one_by_stage("EditableFormField", "Stage", "\"EditableFormField\".\"ID\" = $text->ID");
|
||||||
|
|
||||||
|
$this->assertEquals($check->Title, $revertTo);
|
||||||
|
|
||||||
|
// check the edited buttoned
|
||||||
|
$liveForm = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
|
||||||
|
$revertedForm = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
|
||||||
|
|
||||||
|
$this->assertEquals($liveForm->SubmitButtonText, $revertedForm->SubmitButtonText);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testDuplicatingForm() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$duplicate = $form->duplicate();
|
||||||
|
|
||||||
|
$this->assertEquals($form->Fields()->Count(), $duplicate->Fields()->Count());
|
||||||
|
$this->assertEquals($form->EmailRecipients()->Count(), $form->EmailRecipients()->Count());
|
||||||
|
|
||||||
|
// can't compare object since the dates/ids change
|
||||||
|
$this->assertEquals($form->Fields()->First()->Title, $duplicate->Fields()->First()->Title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo once getIsModifiedOnStage is implemented will need to implement this
|
||||||
|
*/
|
||||||
|
function testGetIsModifiedOnStage() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$this->assertTrue($form->getIsModifiedOnStage());
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFormOptions() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
|
||||||
|
|
||||||
|
$fields = $form->getFormOptions();
|
||||||
|
$submit = $fields->fieldByName('SubmitButtonText');
|
||||||
|
$reset = $fields->fieldByName('ShowClearButton');
|
||||||
|
|
||||||
|
$this->assertEquals($submit->Title(), 'Text on submit button:');
|
||||||
|
$this->assertEquals($reset->Title(), 'Show Clear Form Button');
|
||||||
|
}
|
||||||
|
}
|
87
tests/UserDefinedFormTest.yml
Normal file
87
tests/UserDefinedFormTest.yml
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
UserDefinedForm_EmailRecipient:
|
||||||
|
recipient-1:
|
||||||
|
EmailAddress: test@example.com
|
||||||
|
EmailSubject: Email Subject
|
||||||
|
EmailFrom: no-reply@example.com
|
||||||
|
|
||||||
|
no-html:
|
||||||
|
EmailAddress: nohtml@example.com
|
||||||
|
EmailSubject: Email Subject
|
||||||
|
EmailFrom: no-reply@example.com
|
||||||
|
SendPlain: true
|
||||||
|
|
||||||
|
no-data:
|
||||||
|
EmailAddress: nodata@example.com
|
||||||
|
EmailSubject: Email Subject
|
||||||
|
EmailFrom: no-reply@example.com
|
||||||
|
HideFormData: true
|
||||||
|
|
||||||
|
EditableTextField:
|
||||||
|
basic-text:
|
||||||
|
Name: basic-text-name
|
||||||
|
Title: Basic Text Field
|
||||||
|
|
||||||
|
required-text:
|
||||||
|
Name: required-text-field
|
||||||
|
Title: Required Text Field
|
||||||
|
CustomErrorMessage: Custom Error Message
|
||||||
|
RightTitle: Right Title
|
||||||
|
Required: true
|
||||||
|
|
||||||
|
EditableDropdown:
|
||||||
|
basic-dropdown:
|
||||||
|
Name: basic-dropdown
|
||||||
|
Title: Basic Dropdown Field
|
||||||
|
Options: =>EditableOption.option-1, =>EditableOption.option-2
|
||||||
|
|
||||||
|
department-dropdown:
|
||||||
|
Name: department
|
||||||
|
Title: Department
|
||||||
|
Options: =>EditableOption.department-1, =>EditableOption.department-2
|
||||||
|
|
||||||
|
EditableCheckbox:
|
||||||
|
checkbox-1:
|
||||||
|
Name: checkbox-1
|
||||||
|
Title: Checkbox 1
|
||||||
|
|
||||||
|
EditableEmailField:
|
||||||
|
email-field:
|
||||||
|
Name: email-field
|
||||||
|
Title: Email
|
||||||
|
|
||||||
|
UserDefinedForm:
|
||||||
|
basic-form-page:
|
||||||
|
Title: User Defined Form
|
||||||
|
Fields: =>EditableTextField.basic-text
|
||||||
|
EmailRecipients: =>UserDefinedForm_EmailRecipient.recipient-1, =>UserDefinedForm_EmailRecipient.no-html, =>UserDefinedForm_EmailRecipient.no-data
|
||||||
|
|
||||||
|
form-with-reset-and-custom-action:
|
||||||
|
Title: Form with Reset Action
|
||||||
|
SubmitButtonText: Custom Button
|
||||||
|
ShowClearButton: true
|
||||||
|
|
||||||
|
validation-form:
|
||||||
|
Title: Validation Form
|
||||||
|
Fields: =>EditableTextField.required-text
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
5
tests/templates/UserDefinedFormControllerTest.ss
Normal file
5
tests/templates/UserDefinedFormControllerTest.ss
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
$Content
|
||||||
|
$Form
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user