FEATURE: added ability to have rules on userdefined form fields

This commit is contained in:
Will Rossiter 2009-04-23 22:52:08 +00:00
parent 4b1a806444
commit d592ed005f
7 changed files with 321 additions and 23 deletions

View File

@ -281,11 +281,13 @@ class UserDefinedForm_Controller extends Page_Controller {
$fields = new FieldSet();
$fieldValidation = array();
$fieldValidationRules = array();
$CustomDisplayRules = "";
$defaults = "";
$this->SubmitButtonText = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
if($this->Fields()) {
foreach($this->Fields() as $field) {
$fieldToAdd = $field->getFormField();
$fieldValidationOptions = array();
@ -310,9 +312,58 @@ class UserDefinedForm_Controller extends Page_Controller {
if($fieldValidationOptions) {
$fieldValidationRules[$field->Name] = $fieldValidationOptions;
}
// Is this Field Show by Default
if(!$field->ShowOnLoad) {
$defaults .= "$(\"#" . $field->Name . "\").hide();\n";
}
// Check for field dependencies / default
if($field->Dependencies()) {
foreach($field->Dependencies() as $dependency) {
if(is_array($dependency) && isset($dependency['ConditionField']) && $dependency['ConditionField'] != "") {
// get the field which is effected
$formName = Convert::raw2sql($dependency['ConditionField']);
$formFieldWatch = DataObject::get_one("EditableFormField", "Name = '$formName'");
if(!$formFieldWatch) break;
$fieldToWatch = "$(\"#Form_Form_".$dependency['ConditionField']."\")";
// show or hide?
$view = (isset($dependency['Display']) && $dependency['Display'] == "Show") ? "show" : "hide";
$opposite = ($view == "show") ? "hide" : "show";
$Action = ($formFieldWatch->ClassName == "EditableTextField") ? "keyup" : "change";
switch($dependency['ConditionOption']) {
case 'IsNotBlank':
$matches = '!= ""';
break;
case 'IsBlank':
$matches = '== ""';
break;
case 'HasValue':
$matches = '== "'. $dependency['Value'] .'"';
break;
default:
$matches = '!= "'. $dependency['Value'] .'"';
break;
}
// put it all together
$CustomDisplayRules .= $fieldToWatch.".$Action(function() {
if($(this).val() ". $matches ." ) {
$(\"#". $field->Name ."\").".$view."();
}
else {
$(\"#". $field->Name ."\").".$opposite."();
}
});";
}
}
}
}
}
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
// Keep track of the referer
@ -332,14 +383,17 @@ class UserDefinedForm_Controller extends Page_Controller {
$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
(function($) {
$(document).ready(function() {
$defaults
$("#$FormName").validate({
errorClass: "required",
messages:
@ -349,6 +403,7 @@ class UserDefinedForm_Controller extends Page_Controller {
rules:
$rules
});
$CustomDisplayRules
});
})(jQuery);
JS
@ -372,7 +427,7 @@ JS
$value = (is_bool($value)) ? $value : "\"$value\"";
$result[] = "$key:$value \n";
}
return (isset($result)) ? "{\n".implode( ', ', $result ) ."} \n": '';
return (isset($result)) ? "{\n".implode( ', ', $result ) ."} \n": '{}';
}
/**

View File

@ -17,8 +17,9 @@ class EditableFormField extends DataObject {
"Required" => "Boolean",
"CanDelete" => "Boolean",
"CustomParameter" => "Varchar",
"OptionallyDisplay" => "Boolean",
"CustomErrorMessage" => "Varchar(255)"
"CustomErrorMessage" => "Varchar(255)",
"CustomRules" => "Text",
"ShowOnLoad" => "Boolean",
);
static $defaults = array(
@ -99,6 +100,56 @@ class EditableFormField extends DataObject {
public function showExtraOptions() {
return true;
}
/**
* Return the Custom Validation fields for this
* field for the CMS
*
* @return array
*/
public function Dependencies() {
return ($this->CustomRules) ? unserialize($this->CustomRules) : array();
}
/**
* Return the custom validation fields for the field
*
* @return DataObjectSet
*/
public function CustomRules() {
$output = new DataObjectSet();
$fields = $this->Parent()->Fields();
// add the default add
$output->push(new ArrayData(array(
'Name' => $this->Name(),
'AddableOption' => true,
'Fields' => $fields
)));
// check for existing ones
if($this->CustomRules) {
$rules = unserialize($this->CustomRules);
if($rules) {
foreach($rules as $rule => $data) {
// recreate all the field object to prevent caching
$outputFields = new DataObjectSet();
foreach($fields as $field) {
$new = clone $field;
$new->isSelected = ($new->Name == $data['ConditionField']) ? true : false;
$outputFields->push($new);
}
$output->push(new ArrayData(array(
'Name' => $this->Name(),
'Display' => $data['Display'],
'Fields' => $outputFields,
'ConditionField' => $data['ConditionField'],
'ConditionOption' => $data['ConditionOption'],
'Value' => $data['Value']
)));
}
}
}
return $output;
}
function makeReadonly() {
$this->readonly = true;
@ -117,10 +168,23 @@ class EditableFormField extends DataObject {
return "<input type=\"text\" class=\"text\" title=\"("._t('EditableFormField.ENTERQUESTION', 'Enter Question').")\" value=\"$titleAttr\" name=\"Fields[{$this->ID}][Title]\"$readOnlyAttr />";
}
function Name() {
/**
* Return the base name for this form field in the
* form builder
*
* @return String
*/
public function Name() {
return "Fields[".$this->ID."]";
}
/**
* @todo Fix this - shouldn't name be returning name?!?
*/
public function BaseName() {
return $this->Name;
}
/**
* How to save the data submitted in this field into
* the database object which this field represents.
@ -140,6 +204,28 @@ class EditableFormField extends DataObject {
$this->CanDelete = (isset($data['CanDelete']) && !$data['CanDelete']) ? 0 : 1;
$this->Name = $this->class.$this->ID;
$this->CustomErrorMessage = (isset($data['CustomErrorMessage'])) ? $data['CustomErrorMessage'] : "";
$this->CustomRules = "";
$this->ShowOnLoad = (isset($data['ShowOnLoad']) && $data['ShowOnLoad'] == "Show") ? 1 : 0;
// custom validation
if(isset($data['CustomRules'])) {
$rules = array();
foreach($data['CustomRules'] as $key => $value) {
if(is_array($value)) {
$fieldValue = (isset($value['Value'])) ? $value['Value'] : '';
if(isset($value['ConditionOption']) && $value['ConditionOption'] == "Blank" || $value['ConditionOption'] == "NotBlank") {
$fieldValue = "";
}
$rules[] = array(
'Display' => (isset($value['Display'])) ? $value['Display'] : "",
'ConditionField' => (isset($value['ConditionField'])) ? $value['ConditionField'] : "",
'ConditionOption' => (isset($value['ConditionOption'])) ? $value['ConditionOption'] : "",
'Value' => $fieldValue
);
}
}
$this->CustomRules = serialize($rules);
}
$this->write();
}
@ -148,6 +234,7 @@ class EditableFormField extends DataObject {
$baseName = "Fields[$this->ID]";
$extraOptions = new FieldSet();
// Is this field required
if(!$this->Parent()->hasMethod('hideExtraOption')){
$extraOptions->push(new CheckboxField($baseName . "[Required]", _t('EditableFormField.REQUIRED', 'Required?'), $this->Required));
}
@ -167,12 +254,9 @@ class EditableFormField extends DataObject {
$extraOptions = $extraOptions->makeReadonly();
}
// support for optionally display field
// $extraOptions->push(new CheckboxField($baseName ."[OptionallyDisplay]", _t('EditableFormField.OPTIONALLYDISPLAY', 'Optionally Display Field'), $this->OptionallyDisplay));
// support for custom error messaging
// custom error messaging
$extraOptions->push(new TextField($baseName.'[CustomErrorMessage]', _t('EditableFormField.CUSTOMERROR','Custom Error Message'), $this->CustomErrorMessage));
return $extraOptions;
}
@ -201,12 +285,7 @@ class EditableFormField extends DataObject {
* @todo: escape the string
*/
function filterClause( $value ) {
// Not filtering on this field
if( $value == '-1' )
return "";
else
return "`{$this->name}` = '$value'";
return ($value == '-1') ? "" : "`{$this->name}` = '$value'";
}
function showInReports() {

View File

@ -202,7 +202,7 @@ class FieldEditor extends FormField {
}
return false;
}
function setHaveFormOptions($bool){
$this->haveFormOptions = $bool;
}

View File

@ -113,6 +113,9 @@
padding: 3px 0;
overflow: hidden;
}
#Fields_fields .EditableFormField .extraOptions a {
background: none;
}
#Fields_fields .EditableFormField .extraOptions input {
font-size: 11px;
padding: 2px;
@ -137,7 +140,37 @@
margin: 0 8px 0 0;
}
/* CUSTOM RULES */
#Fields_fields .customRules {
clear: both;
}
#Fields_fields .customRules li {
clear: both;
}
#Fields_fields .customRules li.firstField {
padding-bottom: 5px;
margin: 0 5px;
border-bottom: 1px solid #bbb;
}
#Fields_fields .customRules label {
float: left;
margin: 0;
padding: 2px 4px;
font-size: 11px;
}
#Fields_fields .customRules select {
float: left;
font-size: 11px;
width: 120px;
margin-right: 4px;
}
#Fields_fields .customRules a {
background: none;
width: 20px;
float: left;
}
/* HIDE */
#Fields_fields li.EditableFormField .hidden {
display: none;
}

View File

@ -55,6 +55,13 @@
success: function(msg){
$('#Fields_fields').append(msg);
statusMessage(ss.i18n._t('UserForms.ADDEDNEWFIELD', 'Added New Field'));
//update the internal lists
var name = $("#Fields_fields li.EditableFormField:last").attr("id").split(' ');
//$("#Fields_fields select.fieldOption").each(function(i, domElement) {
// $(domElement).append("<option='"+ name[2] +"'>New "+ name[2] + "</option>");
//});
},
// error creating new field
@ -62,8 +69,22 @@
statusMessage(ss.i18n._t('UserForms.ERRORCREATINGFIELD', 'Error Creating Field'));
}
});
});
/**
* Upon renaming a field we should go through and rename all the
* fields in the select fields to use this new field title. We can
* just worry about the title text - don't mess around with the keys
*/
$('.EditableFormField .fieldInfo .text').livequery('change', function() {
var value = $(this).val();
var name = $(this).parents("li").attr("id").split(' ');
$("#Fields_fields select.fieldOption option").each(function(i, domElement) {
if($(domElement).val() == name[2]) {
$(domElement).text(value);
}
});
})
/**
* Show the more options popdown. Or hide it if we
* currently have it open
@ -93,6 +114,21 @@
* Delete a field from the user defined form
*/
$(".EditableFormField .delete").livequery('click', function() {
// remove all the rules with relate to this field
var text = $(this).parents("li").find(".fieldInfo .text").val();
$("#Fields_fields .customRules select.fieldOption option").each(function(i, domElement) {
if($(domElement).text() == text) {
// check to see if this is selected. If it is then just remove the whole rule
if($(domElement).parent('select.customRuleField').val() == $(domElement).val()) {
$(domElement).parents('li.customRule').remove();
}
// otherwise remove the option
else {
$(domElement).remove();
}
}
});
$(this).parents(".EditableFormField").remove();
return false;
});
@ -198,6 +234,57 @@
}
});
});
/**
* Custom Rules Interface
*/
$(".customRules .conditionOption").livequery('change', function(){
var valueInput = $(this).siblings(".ruleValue");
if($(this).val() == "ValueNot" || $(this).val() == "HasValue") {
valueInput.show();
}
else {
valueInput.hide();
}
});
/**
* Delete a custom rule
*/
$(".customRules .deleteCondition").livequery('click', function() {
$(this).parent("li").fadeOut().remove();
});
/**
* Adding a custom rule to a given form
*/
$(".customRules .addCondition").livequery('click', function() {
// Give the user some feedback
statusMessage(ss.i18n._t('UserForms.ADDINGNEWRULE', 'Adding New Rule'));
// get the parent li which to duplicate
var parent = $(this).parent("li");
var grandParent = parent.parent("ul");
var newCondition = parent.clone();
// remove add icon
newCondition.find(".addCondition").hide();
newCondition.find("a.hidden").removeClass("hidden");
newCondition.children(".customRuleField").each(function(i, domElement) {
// go through and fix names. We need to insert an id number into the middle of them at least
$(domElement).val($(parent).find("select").eq(i).val());
var currentName = domElement.name.split("][");
currentName[3] = currentName[2];
currentName[2] = grandParent.children().size() + 1;
domElement.name = currentName.join("][");
});
grandParent.append(newCondition);
// clear fields
parent.each(function(i, domElement) {
$(domElement).find(".customRuleField").val("");
});
return false;
});
});
})
(jQuery);

View File

@ -1,4 +1,5 @@
<li class="$ClassName EditableFormField" id="$Name.Attr EditableItem_$Pos">
<!-- JS Relys on EditableFormField as a class - and the 3 ids in this order - do not change -->
<li class="$ClassName EditableFormField" id="$Name.Attr EditableItem_$Pos $BaseName">
<div class="fieldInfo">
<% if isReadonly %>
<img class="fieldHandler" src="sapphire/images/drag_readonly.gif" alt="<% _t('LOCKED', 'These fields cannot be modified') %>" />
@ -43,10 +44,27 @@
<% end_if %>
<% end_if %>
</ul>
<% control ExtraOptions %>
$FieldHolder
<% end_control %>
<div class="customRules">
<h4>Custom Rules</h4>
<select name="$Name.Attr[ShowOnLoad]">
<option value="Show" <% if ShowOnLoad %>selected="selected"<% end_if %>><% _t('SHOW', 'Show') %></option>
<option value="Hide" <% if ShowOnLoad %><% else %>selected="selected"<% end_if %>><% _t('HIDE', 'Hide') %></option>
</select>
<label class="left">Field On Default</label>
<ul id="$Name.Attr-customRules">
<% control CustomRules %>
<li class="customRule">
<% include CustomRule %>
</li>
<% end_control %>
</ul>
</div>
</div>
<% end_if %>

View File

@ -0,0 +1,26 @@
<select class="displayOption customRuleField" name="{$Name}[CustomRules]<% if First %><% else %><% if Pos %>[$Pos]<% end_if %><% end_if %>[Display]">
<option value="Show" <% if Display = Show %>selected="selected"<% end_if %>><% _t('SHOWTHISFIELD', 'Show This Field') %></option>
<option value="Hide" <% if Display = Hide %><% if First %><% else %>selected="selected"<% end_if %><% end_if %>><% _t('HIDETHISFIELD', 'Hide This Field') %></option>
</select>
<label><% _t('WHEN', 'When') %></label>
<select class="fieldOption customRuleField" name="{$Name}[CustomRules]<% if First %><% else %><% if Pos %>[$Pos]<% end_if %><% end_if %>[ConditionField]">
<option value="" selected="selected"></option>
<% control Fields %>
<option value="$BaseName" <% if isSelected %>selected="selected"<% end_if %>>$Title</option>
<% end_control %>
</select>
<label><% _t('IS', 'Is') %></label>
<select class="conditionOption customRuleField" name="{$Name}[CustomRules]<% if First %><% else %><% if Pos %>[$Pos]<% end_if %><% end_if %>[ConditionOption]">
<option value=""></option>
<option value="IsBlank" <% if ConditionOption = IsBlank %>selected="selected"<% end_if %>><% _t('BLANK', 'Blank') %></option>
<option value="IsNotBlank" <% if ConditionOption = IsNotBlank %>selected="selected"<% end_if %>><% _t('NOTBLANK', 'Not Blank') %></option>
<option value="HasValue" <% if ConditionOption = HasValue %>selected="selected"<% end_if %>><% _t('VALUE', 'Value') %></option>
<option value="ValueNot" <% if ConditionOption = ValueNot %>selected="selected"<% end_if %>><% _t('NOTVALUE', 'Not Value') %></option>
</select>
<input type="text" class="ruleValue <% if Value %><% else %>hidden<% end_if %> customRuleField" name="{$Name}[CustomRules]<% if First %><% else %><% if Pos %>[$Pos]<% end_if %><% end_if %>[Value]" value="$Value" />
<a href="#" class="addCondition <% if First %><% else %>hidden<% end_if %>" title="<% _t('ADD', 'Add') %>"><img src="cms/images/add.gif" alt="<% _t('ADD', 'Add') %>" /></a>
<a href="#" class="deleteCondition <% if First %>hidden<% end_if %>" title="<% _t('DELETE', 'Delete') %>"><img src="cms/images/delete.gif" alt="<% _t('DELETE', 'Delete') %>" /></a>