From d592ed005f9890b9bdd58ef07c3902cb61c3c571 Mon Sep 17 00:00:00 2001 From: Will Rossiter Date: Thu, 23 Apr 2009 22:52:08 +0000 Subject: [PATCH] FEATURE: added ability to have rules on userdefined form fields --- code/UserDefinedForm.php | 63 ++++++++++++++++-- code/editor/EditableFormField.php | 107 ++++++++++++++++++++++++++---- code/editor/FieldEditor.php | 2 +- css/FieldEditor.css | 35 +++++++++- javascript/UserForm.js | 89 ++++++++++++++++++++++++- templates/EditableFormField.ss | 22 +++++- templates/Includes/CustomRule.ss | 26 ++++++++ 7 files changed, 321 insertions(+), 23 deletions(-) create mode 100644 templates/Includes/CustomRule.ss diff --git a/code/UserDefinedForm.php b/code/UserDefinedForm.php index 262f02d..a08a4f9 100755 --- a/code/UserDefinedForm.php +++ b/code/UserDefinedForm.php @@ -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(<< "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 "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() { diff --git a/code/editor/FieldEditor.php b/code/editor/FieldEditor.php index 42167f2..171ac08 100755 --- a/code/editor/FieldEditor.php +++ b/code/editor/FieldEditor.php @@ -202,7 +202,7 @@ class FieldEditor extends FormField { } return false; } - + function setHaveFormOptions($bool){ $this->haveFormOptions = $bool; } diff --git a/css/FieldEditor.css b/css/FieldEditor.css index 9f93ee4..2577284 100755 --- a/css/FieldEditor.css +++ b/css/FieldEditor.css @@ -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; } diff --git a/javascript/UserForm.js b/javascript/UserForm.js index 26dc2df..9dfd81b 100644 --- a/javascript/UserForm.js +++ b/javascript/UserForm.js @@ -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("New "+ name[2] + ""); + //}); }, // 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); \ No newline at end of file diff --git a/templates/EditableFormField.ss b/templates/EditableFormField.ss index 6ea551e..3d4471e 100755 --- a/templates/EditableFormField.ss +++ b/templates/EditableFormField.ss @@ -1,4 +1,5 @@ -
  • + +
  • <% if isReadonly %> <% _t('LOCKED', 'These fields cannot be modified') %> @@ -43,10 +44,27 @@ <% end_if %> <% end_if %> - + <% control ExtraOptions %> $FieldHolder <% end_control %> + +
    +

    Custom Rules

    + + + +
      + <% control CustomRules %> +
    • + <% include CustomRule %> +
    • + <% end_control %> +
    +
    <% end_if %> diff --git a/templates/Includes/CustomRule.ss b/templates/Includes/CustomRule.ss new file mode 100644 index 0000000..406dae2 --- /dev/null +++ b/templates/Includes/CustomRule.ss @@ -0,0 +1,26 @@ + + + + + + + + + + +<% _t('ADD', 'Add') %> +<% _t('DELETE', 'Delete') %>