mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
Enhancement: Implemented and/or display rules for UserForms
This commit is contained in:
parent
916dbf42c7
commit
a94f0e35aa
@ -6,6 +6,10 @@
|
||||
*
|
||||
* @method EditableFormField Parent()
|
||||
* @package userforms
|
||||
*
|
||||
* @property string Display
|
||||
* @property string ConditionOption
|
||||
* @property string FieldValue
|
||||
*/
|
||||
class EditableCustomRule extends DataObject
|
||||
{
|
||||
@ -147,4 +151,91 @@ class EditableCustomRule extends DataObject
|
||||
{
|
||||
return $this->canDelete($member);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitutes configured rule logic with it's JS equivalents and returns them as array elements
|
||||
* @return array
|
||||
*/
|
||||
public function buildExpression()
|
||||
{
|
||||
/** @var EditableFormField $formFieldWatch */
|
||||
$formFieldWatch = $this->ConditionField();
|
||||
//Encapsulated the action to the object
|
||||
$action = $formFieldWatch->getJsEventHandler();
|
||||
|
||||
// is this field a special option field
|
||||
$checkboxField = $formFieldWatch->isCheckBoxField();
|
||||
$radioField = $formFieldWatch->isRadioField();
|
||||
$target = sprintf('$("%s")', $formFieldWatch->getSelectorFieldOnly());
|
||||
$fieldValue = Convert::raw2js($this->FieldValue);
|
||||
|
||||
$conditionOptions = array(
|
||||
'ValueLessThan' => '<',
|
||||
'ValueLessThanEqual' => '<=',
|
||||
'ValueGreaterThan' => '>',
|
||||
'ValueGreaterThanEqual' => '>='
|
||||
);
|
||||
// and what should we evaluate
|
||||
switch ($this->ConditionOption) {
|
||||
case 'IsNotBlank':
|
||||
case 'IsBlank':
|
||||
$expression = ($checkboxField || $radioField) ? "!{$target}.is(\":checked\")" : "{$target}.val() == ''";
|
||||
if ($this->ConditionOption == 'IsNotBlank') {
|
||||
//Negate
|
||||
$expression = "!({$expression})";
|
||||
}
|
||||
break;
|
||||
case 'HasValue':
|
||||
case 'ValueNot':
|
||||
if ($checkboxField) {
|
||||
if ($formFieldWatch->isCheckBoxGroupField()) {
|
||||
$expression = sprintf("$.inArray('%s', %s.filter(':checked').map(function(){ return $(this).val();}).get()) > -1",
|
||||
$fieldValue, $target);
|
||||
} else {
|
||||
$expression = "{$target}.prop('checked')";
|
||||
}
|
||||
} elseif ($radioField) {
|
||||
// We cannot simply get the value of the radio group, we need to find the checked option first.
|
||||
$expression = sprintf('%s.closest(".field, .control-group").find("input:checked").val() == "%s"',
|
||||
$target, $fieldValue);
|
||||
} else {
|
||||
$expression = sprintf('%s.val() == "%s"', $target, $fieldValue);
|
||||
}
|
||||
|
||||
if ($this->ConditionOption == 'ValueNot') {
|
||||
//Negate
|
||||
$expression = "!({$expression})";
|
||||
}
|
||||
break;
|
||||
case 'ValueLessThan':
|
||||
case 'ValueLessThanEqual':
|
||||
case 'ValueGreaterThan':
|
||||
case 'ValueGreaterThanEqual':
|
||||
$expression = sprintf('%s.val() %s parseFloat("%s")', $target,
|
||||
$conditionOptions[$this->ConditionOption], $fieldValue);
|
||||
break;
|
||||
default:
|
||||
throw new LogicException("Unhandled rule {$this->ConditionOption}");
|
||||
break;
|
||||
}
|
||||
|
||||
$result = array(
|
||||
'operation' => $expression,
|
||||
'event' => $action,
|
||||
);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the opposite of the show/hide pairs of strings
|
||||
*
|
||||
* @param string $text
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toggleDisplayText($text)
|
||||
{
|
||||
return (strtolower($text) === 'show') ? 'hide' : 'show';
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
|
||||
class UserDefinedForm extends Page
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@ -454,183 +454,29 @@ class UserDefinedForm_Controller extends Page_Controller
|
||||
$rules = "";
|
||||
|
||||
$watch = array();
|
||||
$watchLoad = array();
|
||||
|
||||
if ($this->Fields()) {
|
||||
/** @var EditableFormField $field */
|
||||
foreach ($this->Fields() as $field) {
|
||||
$holderSelector = $field->getSelectorHolder();
|
||||
|
||||
// Is this Field Show by Default
|
||||
if (!$field->ShowOnLoad) {
|
||||
$default .= "{$holderSelector}.hide().trigger('userform.field.hide');\n";
|
||||
}
|
||||
|
||||
// Check for field dependencies / default
|
||||
foreach ($field->EffectiveDisplayRules() as $rule) {
|
||||
|
||||
// Get the field which is effected
|
||||
$formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID);
|
||||
|
||||
// Skip deleted fields
|
||||
if (!$formFieldWatch) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldToWatch = $formFieldWatch->getSelectorField($rule);
|
||||
$fieldToWatchOnLoad = $formFieldWatch->getSelectorField($rule, true);
|
||||
|
||||
// show or hide?
|
||||
$view = ($rule->Display == 'Hide') ? 'hide' : 'show';
|
||||
$opposite = ($view == "show") ? "hide" : "show";
|
||||
|
||||
// what action do we need to keep track of. Something nicer here maybe?
|
||||
// @todo encapulsation
|
||||
$action = "change";
|
||||
|
||||
if ($formFieldWatch instanceof EditableTextField) {
|
||||
$action = "keyup";
|
||||
}
|
||||
|
||||
// is this field a special option field
|
||||
$checkboxField = false;
|
||||
$radioField = false;
|
||||
|
||||
if (in_array($formFieldWatch->ClassName, array('EditableCheckboxGroupField', 'EditableCheckbox'))) {
|
||||
$action = "click";
|
||||
$checkboxField = true;
|
||||
} elseif ($formFieldWatch->ClassName == "EditableRadioField") {
|
||||
$radioField = true;
|
||||
}
|
||||
|
||||
// and what should we evaluate
|
||||
switch ($rule->ConditionOption) {
|
||||
case 'IsNotBlank':
|
||||
$expression = ($checkboxField || $radioField) ? '$(this).is(":checked")' :'$(this).val() != ""';
|
||||
|
||||
break;
|
||||
case 'IsBlank':
|
||||
$expression = ($checkboxField || $radioField) ? '!($(this).is(":checked"))' : '$(this).val() == ""';
|
||||
|
||||
break;
|
||||
case 'HasValue':
|
||||
if ($checkboxField) {
|
||||
$expression = '$(this).prop("checked")';
|
||||
} elseif ($radioField) {
|
||||
// We cannot simply get the value of the radio group, we need to find the checked option first.
|
||||
$expression = '$(this).parents(".field, .control-group").find("input:checked").val()=="'. $rule->FieldValue .'"';
|
||||
} else {
|
||||
$expression = '$(this).val() == "'. $rule->FieldValue .'"';
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ValueLessThan':
|
||||
$expression = '$(this).val() < parseFloat("'. $rule->FieldValue .'")';
|
||||
|
||||
break;
|
||||
case 'ValueLessThanEqual':
|
||||
$expression = '$(this).val() <= parseFloat("'. $rule->FieldValue .'")';
|
||||
|
||||
break;
|
||||
case 'ValueGreaterThan':
|
||||
$expression = '$(this).val() > parseFloat("'. $rule->FieldValue .'")';
|
||||
|
||||
break;
|
||||
case 'ValueGreaterThanEqual':
|
||||
$expression = '$(this).val() >= parseFloat("'. $rule->FieldValue .'")';
|
||||
|
||||
break;
|
||||
default: // ==HasNotValue
|
||||
if ($checkboxField) {
|
||||
$expression = '!$(this).prop("checked")';
|
||||
} elseif ($radioField) {
|
||||
// We cannot simply get the value of the radio group, we need to find the checked option first.
|
||||
$expression = '$(this).parents(".field, .control-group").find("input:checked").val()!="'. $rule->FieldValue .'"';
|
||||
} else {
|
||||
$expression = '$(this).val() != "'. $rule->FieldValue .'"';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isset($watch[$fieldToWatch])) {
|
||||
$watch[$fieldToWatch] = array();
|
||||
}
|
||||
|
||||
$watch[$fieldToWatch][] = array(
|
||||
'expression' => $expression,
|
||||
'holder_selector' => $holderSelector,
|
||||
'view' => $view,
|
||||
'opposite' => $opposite,
|
||||
'action' => $action
|
||||
);
|
||||
|
||||
$watchLoad[$fieldToWatchOnLoad] = true;
|
||||
if ($result = $field->formatDisplayRules()) {
|
||||
$watch[] = $result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($watch) {
|
||||
foreach ($watch as $key => $values) {
|
||||
$logic = array();
|
||||
$actions = array();
|
||||
|
||||
foreach ($values as $rule) {
|
||||
// Assign action
|
||||
$actions[$rule['action']] = $rule['action'];
|
||||
|
||||
// Assign behaviour
|
||||
$expression = $rule['expression'];
|
||||
$holder = $rule['holder_selector'];
|
||||
$view = $rule['view']; // hide or show
|
||||
$opposite = $rule['opposite'];
|
||||
// Generated javascript for triggering visibility
|
||||
$logic[] = <<<"EOS"
|
||||
if({$expression}) {
|
||||
{$holder}
|
||||
.{$view}()
|
||||
.trigger('userform.field.{$view}');
|
||||
} else {
|
||||
{$holder}
|
||||
.{$opposite}()
|
||||
.trigger('userform.field.{$opposite}');
|
||||
}
|
||||
EOS;
|
||||
}
|
||||
|
||||
$logic = implode("\n", $logic);
|
||||
$rules .= $key.".each(function() {\n
|
||||
$(this).data('userformConditions', function() {\n
|
||||
$logic\n
|
||||
}); \n
|
||||
});\n";
|
||||
foreach ($actions as $action) {
|
||||
$rules .= $key.".$action(function() {
|
||||
$(this).data('userformConditions').call(this);\n
|
||||
});\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($watchLoad) {
|
||||
foreach ($watchLoad as $key => $value) {
|
||||
$rules .= $key.".each(function() {
|
||||
$(this).data('userformConditions').call(this);\n
|
||||
});\n";
|
||||
}
|
||||
$rules .= $this->buildWatchJS($watch);
|
||||
}
|
||||
|
||||
// Only add customScript if $default or $rules is defined
|
||||
if ($default || $rules) {
|
||||
if ($rules) {
|
||||
Requirements::customScript(<<<JS
|
||||
(function($) {
|
||||
$(document).ready(function() {
|
||||
$default
|
||||
|
||||
$rules
|
||||
})
|
||||
})(jQuery);
|
||||
(function($) {
|
||||
$(document).ready(function() {
|
||||
{$rules}
|
||||
});
|
||||
})(jQuery);
|
||||
JS
|
||||
, 'UserFormsConditional');
|
||||
, 'UserFormsConditional');
|
||||
}
|
||||
}
|
||||
|
||||
@ -727,7 +573,7 @@ JS
|
||||
foreach ($recipients as $recipient) {
|
||||
$email = new UserFormRecipientEmail($submittedFields);
|
||||
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']);
|
||||
|
||||
|
||||
if ($attachments) {
|
||||
foreach ($attachments as $file) {
|
||||
if ($file->ID != 0) {
|
||||
@ -739,7 +585,7 @@ JS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields);
|
||||
|
||||
if (!$recipient->SendPlain && $recipient->emailTemplateExists()) {
|
||||
@ -897,4 +743,46 @@ JS
|
||||
'Form' => '',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the required JS from the $watch input
|
||||
*
|
||||
* @param array $watch
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildWatchJS($watch)
|
||||
{
|
||||
$result = '';
|
||||
foreach ($watch as $key => $rule) {
|
||||
$events = implode(' ', $rule['events']);
|
||||
$selectors = implode(', ', $rule['selectors']);
|
||||
$conjunction = $rule['conjunction'];
|
||||
$operations = implode(" {$conjunction} ", $rule['operations']);
|
||||
$target = $rule['targetFieldID'];
|
||||
$initialState = $rule['initialState'];
|
||||
$view = $rule['view'];
|
||||
$opposite = $rule['opposite'];
|
||||
|
||||
$result .= <<<EOS
|
||||
\n
|
||||
//Initial state
|
||||
$('{$target}').{$initialState}();
|
||||
|
||||
$('.userform').on('{$events}',
|
||||
"{$selectors}",
|
||||
function (){
|
||||
if({$operations}) {
|
||||
$('{$target}').{$view}();
|
||||
} else {
|
||||
$('{$target}').{$opposite}();
|
||||
}
|
||||
});
|
||||
EOS;
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ class EditableCheckbox extends EditableFormField
|
||||
|
||||
private static $plural_name = 'Checkboxes';
|
||||
|
||||
protected $jsEventHandler = 'click';
|
||||
|
||||
private static $db = array(
|
||||
'CheckedDefault' => 'Boolean' // from CustomSettings
|
||||
);
|
||||
@ -61,4 +63,8 @@ class EditableCheckbox extends EditableFormField
|
||||
|
||||
parent::migrateSettings($data);
|
||||
}
|
||||
|
||||
public function isCheckBoxField() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField
|
||||
|
||||
private static $plural_name = "Checkbox Groups";
|
||||
|
||||
protected $jsEventHandler = 'click';
|
||||
|
||||
public function getFormField()
|
||||
{
|
||||
$field = new UserFormsCheckboxSetField($this->Name, $this->EscapedTitle, $this->getOptionsMap());
|
||||
@ -59,4 +61,18 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField
|
||||
return "$(\"input[name='{$this->Name}[]']:first\")";
|
||||
}
|
||||
}
|
||||
|
||||
public function isCheckBoxField() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSelectorFieldOnly()
|
||||
{
|
||||
return "[name='{$this->Name}[]']";
|
||||
}
|
||||
|
||||
public function isCheckBoxGroupField()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ use SilverStripe\Forms\SegmentField;
|
||||
* @property int $Sort
|
||||
* @property bool $Required
|
||||
* @property string $CustomErrorMessage
|
||||
* @property boolean $ShowOnLoad
|
||||
* @property string $DisplayRulesConjunction
|
||||
* @method UserDefinedForm Parent() Parent page
|
||||
* @method DataList DisplayRules() List of EditableCustomRule objects
|
||||
*/
|
||||
@ -87,8 +89,10 @@ class EditableFormField extends DataObject
|
||||
"RightTitle" => "Varchar(255)", // from CustomSettings
|
||||
"ShowOnLoad" => "Boolean(1)", // from CustomSettings
|
||||
"ShowInSummary" => "Boolean",
|
||||
'DisplayRulesConjunction' => 'Enum("And,Or","Or")',
|
||||
);
|
||||
|
||||
|
||||
private static $defaults = array(
|
||||
'ShowOnLoad' => true,
|
||||
);
|
||||
@ -125,6 +129,22 @@ class EditableFormField extends DataObject
|
||||
*/
|
||||
protected $readonly;
|
||||
|
||||
/**
|
||||
* Property holds the JS event which gets fired for this type of element
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $jsEventHandler = 'change';
|
||||
|
||||
/**
|
||||
* Returns the jsEventHandler property for the current object. Bearing in mind it could've been overridden.
|
||||
* @return string
|
||||
*/
|
||||
public function getJsEventHandler()
|
||||
{
|
||||
return $this->jsEventHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visibility of an individual form field
|
||||
*
|
||||
@ -244,44 +264,36 @@ class EditableFormField extends DataObject
|
||||
// Check display rules
|
||||
if ($this->Required) {
|
||||
return new FieldList(
|
||||
LabelField::create(_t(
|
||||
'EditableFormField.DISPLAY_RULES_DISABLED',
|
||||
'Display rules are not enabled for required fields. ' .
|
||||
'Please uncheck "Is this field Required?" under "Validation" to re-enable.'
|
||||
))->addExtraClass('message warning')
|
||||
);
|
||||
LabelField::create(
|
||||
_t(
|
||||
'EditableFormField.DISPLAY_RULES_DISABLED',
|
||||
'Display rules are not enabled for required fields. Please uncheck "Is this field Required?" under "Validation" to re-enable.'))
|
||||
->addExtraClass('message warning'));
|
||||
}
|
||||
$self = $this;
|
||||
$allowedClasses = array_keys($this->getEditableFieldClasses(false));
|
||||
$editableColumns = new GridFieldEditableColumns();
|
||||
$editableColumns->setDisplayFields(array(
|
||||
'Display' => '',
|
||||
'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) {
|
||||
return DropdownField::create(
|
||||
$column,
|
||||
'',
|
||||
EditableFormField::get()
|
||||
->filter(array(
|
||||
'ParentID' => $self->ParentID,
|
||||
'ClassName' => $allowedClasses
|
||||
))
|
||||
->exclude(array(
|
||||
'ID' => $self->ID
|
||||
))
|
||||
->map('ID', 'Title')
|
||||
);
|
||||
},
|
||||
'ConditionOption' => function ($record, $column, $grid) {
|
||||
$options = Config::inst()->get('EditableCustomRule', 'condition_options');
|
||||
return DropdownField::create($column, '', $options);
|
||||
},
|
||||
'FieldValue' => function ($record, $column, $grid) {
|
||||
return TextField::create($column);
|
||||
},
|
||||
'ParentID' => function ($record, $column, $grid) use ($self) {
|
||||
return HiddenField::create($column, '', $self->ID);
|
||||
}
|
||||
));
|
||||
'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) {
|
||||
return DropdownField::create($column, '', EditableFormField::get()->filter(array(
|
||||
'ParentID' => $self->ParentID,
|
||||
'ClassName' => $allowedClasses,
|
||||
))->exclude(array(
|
||||
'ID' => $self->ID,
|
||||
))->map('ID', 'Title'));
|
||||
},
|
||||
'ConditionOption' => function ($record, $column, $grid) {
|
||||
$options = Config::inst()->get('EditableCustomRule', 'condition_options');
|
||||
|
||||
return DropdownField::create($column, '', $options);
|
||||
},
|
||||
'FieldValue' => function ($record, $column, $grid) {
|
||||
return TextField::create($column);
|
||||
},
|
||||
'ParentID' => function ($record, $column, $grid) use ($self) {
|
||||
return HiddenField::create($column, '', $self->ID);
|
||||
},
|
||||
));
|
||||
|
||||
// Custom rules
|
||||
$customRulesConfig = GridFieldConfig::create()
|
||||
@ -294,11 +306,20 @@ class EditableFormField extends DataObject
|
||||
);
|
||||
|
||||
return new FieldList(
|
||||
CheckboxField::create('ShowOnLoad')
|
||||
->setDescription(_t(
|
||||
'EditableFormField.SHOWONLOAD',
|
||||
'Initial visibility before processing these rules'
|
||||
)),
|
||||
DropdownField::create('ShowOnLoad',
|
||||
_t('EditableFormField.INITIALVISIBILITY', 'Initial visibility'),
|
||||
array(
|
||||
1 => 'Show',
|
||||
0 => 'Hide',
|
||||
)
|
||||
),
|
||||
DropdownField::create('DisplayRulesConjunction',
|
||||
_t('EditableFormField.DISPLAYIF', 'Toggle visibility when'),
|
||||
array(
|
||||
'Or' => _t('UserDefinedForm.SENDIFOR', 'Any conditions are true'),
|
||||
'And' => _t('UserDefinedForm.SENDIFAND', 'All conditions are true'),
|
||||
)
|
||||
),
|
||||
GridField::create(
|
||||
'DisplayRules',
|
||||
_t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
|
||||
@ -489,6 +510,7 @@ class EditableFormField extends DataObject
|
||||
{
|
||||
$this->publish($fromStage, $toStage, $createNewVersion);
|
||||
|
||||
|
||||
$seenIDs = array();
|
||||
|
||||
// Don't forget to publish the related custom rules...
|
||||
@ -899,18 +921,38 @@ class EditableFormField extends DataObject
|
||||
*/
|
||||
public function getSelectorHolder()
|
||||
{
|
||||
return "$(\"#{$this->Name}\")";
|
||||
return sprintf('$("%s")', $this->getSelectorOnly());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns only the JS identifier of a string, less the $(), which can be inserted elsewhere, for example when you
|
||||
* want to perform selections on multiple selectors
|
||||
* @return string
|
||||
*/
|
||||
public function getSelectorOnly()
|
||||
{
|
||||
return "#{$this->Name}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JS expression for selecting the value for this field
|
||||
*
|
||||
* @param EditableCustomRule $rule Custom rule this selector will be used with
|
||||
* @param bool $forOnLoad Set to true if this will be invoked on load
|
||||
* @param EditableCustomRule $rule Custom rule this selector will be used with
|
||||
* @param bool $forOnLoad Set to true if this will be invoked on load
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
|
||||
{
|
||||
return "$(\"input[name='{$this->Name}']\")";
|
||||
return sprintf("$(%s)", $this->getSelectorFieldOnly());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSelectorFieldOnly()
|
||||
{
|
||||
return "[name='{$this->Name}']";
|
||||
}
|
||||
|
||||
|
||||
@ -970,4 +1012,95 @@ class EditableFormField extends DataObject
|
||||
}
|
||||
return $this->DisplayRules();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts info from DisplayRules into array so UserDefinedForm->buildWatchJS can run through it.
|
||||
* @return array|null
|
||||
*/
|
||||
public function formatDisplayRules()
|
||||
{
|
||||
$holderSelector = $this->getSelectorOnly();
|
||||
$result = array(
|
||||
'targetFieldID' => $holderSelector,
|
||||
'conjunction' => $this->DisplayRulesConjunctionNice(),
|
||||
'selectors' => array(),
|
||||
'events' => array(),
|
||||
'operations' => array(),
|
||||
'initialState' => $this->ShowOnLoadNice(),
|
||||
'view' => array(),
|
||||
'opposite' => array(),
|
||||
);
|
||||
// Check for field dependencies / default
|
||||
/** @var EditableCustomRule $rule */
|
||||
foreach ($this->EffectiveDisplayRules() as $rule) {
|
||||
// Get the field which is effected
|
||||
/** @var EditableFormField $formFieldWatch */
|
||||
$formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID);
|
||||
// Skip deleted fields
|
||||
if (! $formFieldWatch) {
|
||||
continue;
|
||||
}
|
||||
$fieldToWatch = $formFieldWatch->getSelectorFieldOnly();
|
||||
|
||||
$expression = $rule->buildExpression();
|
||||
if (! in_array($fieldToWatch, $result['selectors'])) {
|
||||
$result['selectors'][] = $fieldToWatch;
|
||||
}
|
||||
if (! in_array($expression['event'], $result['events'])) {
|
||||
$result['events'][] = $expression['event'];
|
||||
}
|
||||
$result['operations'][] = $expression['operation'];
|
||||
|
||||
//View/Show should read
|
||||
$result['view'] = $rule->toggleDisplayText($result['initialState']);
|
||||
$result['opposite'] = $rule->toggleDisplayText($result['view']);
|
||||
}
|
||||
|
||||
return (count($result['selectors'])) ? $result : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the set DisplayRulesConjunction with their JS logical operators
|
||||
* @return string
|
||||
*/
|
||||
public function DisplayRulesConjunctionNice()
|
||||
{
|
||||
return (strtolower($this->DisplayRulesConjunction) === 'or') ? '||' : '&&';
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces boolean ShowOnLoad with its JS string equivalent
|
||||
* @return string
|
||||
*/
|
||||
public function ShowOnLoadNice()
|
||||
{
|
||||
return ($this->ShowOnLoad) ? 'show' : 'hide';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this is of type EditableCheckBoxField
|
||||
* @return bool
|
||||
*/
|
||||
public function isCheckBoxField()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this is of type EditableRadioField
|
||||
* @return bool
|
||||
*/
|
||||
public function isRadioField()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determined is this is of type EditableCheckboxGroupField
|
||||
* @return bool
|
||||
*/
|
||||
public function isCheckBoxGroupField()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -46,4 +46,8 @@ class EditableRadioField extends EditableMultipleOptionField
|
||||
$first = $forOnLoad ? ':first' : '';
|
||||
return "$(\"input[name='{$this->Name}']{$first}\")";
|
||||
}
|
||||
|
||||
public function isRadioField() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ class EditableTextField extends EditableFormField
|
||||
'url' => 'Home page'
|
||||
);
|
||||
|
||||
protected $jsEventHandler = 'keyup';
|
||||
|
||||
private static $db = array(
|
||||
'MinLength' => 'Int',
|
||||
'MaxLength' => 'Int',
|
||||
|
31
tests/EditableCustomRuleTest.php
Normal file
31
tests/EditableCustomRuleTest.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class EditableCustomRulesTest
|
||||
*/
|
||||
class EditableCustomRuleTest extends SapphireTest
|
||||
{
|
||||
protected static $fixture_file = 'userforms/tests/EditableCustomRuleTest.yml';
|
||||
|
||||
public function testBuildExpression()
|
||||
{
|
||||
/** @var EditableCustomRule $rule1 */
|
||||
$rule1 = $this->objFromFixture('EditableCustomRule', 'rule1');
|
||||
$result1 = $rule1->buildExpression();
|
||||
|
||||
//Dropdowns expect change event
|
||||
$this->assertEquals('change', $result1['event']);
|
||||
$this->assertNotEmpty($result1['operation']);
|
||||
//Check for equals sign
|
||||
$this->assertContains('==', $result1['operation']);
|
||||
|
||||
/** @var EditableCustomRule $rule2 */
|
||||
$rule2 = $this->objFromFixture('EditableCustomRule', 'rule2');
|
||||
$result2 = $rule2->buildExpression();
|
||||
//TextField expect change event
|
||||
$this->assertEquals('keyup', $result2['event']);
|
||||
$this->assertNotEmpty($result2['operation']);
|
||||
//Check for greater than sign
|
||||
$this->assertContains('>', $result2['operation']);
|
||||
}
|
||||
}
|
30
tests/EditableCustomRuleTest.yml
Normal file
30
tests/EditableCustomRuleTest.yml
Normal file
@ -0,0 +1,30 @@
|
||||
EditableFormField:
|
||||
countryDropdown:
|
||||
ClassName: EditableCountryDropdownField
|
||||
Name: CountrySelection
|
||||
Title: "Choose your country"
|
||||
DisplayRulesConjunction: And
|
||||
ShowOnLoad: false
|
||||
irdNumberField:
|
||||
ClassName: EditableTextField
|
||||
Name: IRDNumber
|
||||
Title: "Enter your IRD Number"
|
||||
countryTextField:
|
||||
ClassName: EditableTextField
|
||||
Name: CountryTextSelection
|
||||
Title: "Enter your country (2 digit prefix)"
|
||||
DisplayRulesConjunction: And
|
||||
ShowOnLoad: false
|
||||
EditableCustomRule:
|
||||
rule1:
|
||||
Display: Show
|
||||
ConditionOption: HasValue
|
||||
FieldValue: NZ
|
||||
ConditionField: =>EditableFormField.countryDropdown
|
||||
Parent: =>EditableFormField.irdNumberField
|
||||
rule2:
|
||||
Display: Show
|
||||
ConditionOption: ValueGreaterThan
|
||||
FieldValue: 1
|
||||
ConditionField: =>EditableFormField.countryTextField
|
||||
Parent: =>EditableFormField.irdNumberField
|
@ -204,4 +204,13 @@ class EditableFormFieldTest extends FunctionalTest
|
||||
$this->assertEquals(10, $attributes['data-rule-minlength']);
|
||||
$this->assertEquals(20, $attributes['data-rule-maxlength']);
|
||||
}
|
||||
|
||||
public function testFormatDisplayRules()
|
||||
{
|
||||
/** @var EditableCheckbox $checkbox */
|
||||
$checkbox = $this->objFromFixture('EditableFormField', 'irdNumberField');
|
||||
$displayRules = $checkbox->formatDisplayRules();
|
||||
$this->assertNotNull($displayRules);
|
||||
$this->assertCount(1, $displayRules['operations']);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,25 @@
|
||||
EditableFormField:
|
||||
irdNumberField:
|
||||
ClassName: EditableTextField
|
||||
Name: IRDNumber
|
||||
Title: "Enter your IRD Number"
|
||||
countryTextField:
|
||||
ClassName: EditableTextField
|
||||
Name: CountryTextSelection
|
||||
Title: "Enter your country (2 digit prefix)"
|
||||
DisplayRulesConjunction: And
|
||||
ShowOnLoad: false
|
||||
EditableCustomRule:
|
||||
rule1:
|
||||
Display: Show
|
||||
ConditionOption: HasValue
|
||||
FieldValue: NZ
|
||||
ConditionField: =>EditableFormField.countryTextField
|
||||
Parent: =>EditableFormField.irdNumberField
|
||||
rule-1:
|
||||
Display: Hide
|
||||
ConditionOption: HasValue
|
||||
FieldValue: 6
|
||||
|
||||
EditableOption:
|
||||
option-1:
|
||||
Name: Option1
|
||||
@ -144,4 +160,4 @@ UserDefinedForm:
|
||||
Title: Custom Rules Form
|
||||
Fields: =>EditableCheckbox.checkbox-with-rule, =>EditableTextField.basic-text-2
|
||||
empty-form:
|
||||
Title: Empty Form
|
||||
Title: Empty Form
|
Loading…
Reference in New Issue
Block a user