Merge remote-tracking branch 'origin/4' into 4.3

This commit is contained in:
Robbie Averill 2017-05-11 14:37:55 +12:00
commit 893a28e0c6
36 changed files with 782 additions and 310 deletions

View File

@ -6,6 +6,10 @@
* *
* @method EditableFormField Parent() * @method EditableFormField Parent()
* @package userforms * @package userforms
*
* @property string Display
* @property string ConditionOption
* @property string FieldValue
*/ */
class EditableCustomRule extends DataObject class EditableCustomRule extends DataObject
{ {
@ -147,4 +151,91 @@ class EditableCustomRule extends DataObject
{ {
return $this->canDelete($member); 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';
}
}

View File

@ -6,7 +6,7 @@
class UserDefinedForm extends Page class UserDefinedForm extends Page
{ {
/** /**
* @var string * @var string
*/ */
@ -205,6 +205,20 @@ SQL;
$config->addComponent($export = new GridFieldExportButton('buttons-after-left')); $config->addComponent($export = new GridFieldExportButton('buttons-after-left'));
$config->addComponent($print = new GridFieldPrintButton('buttons-after-left')); $config->addComponent($print = new GridFieldPrintButton('buttons-after-left'));
// show user form items in the summary tab
$summaryarray = array(
'ID' => 'ID',
'Created' => 'Created',
'LastEdited' => 'Last Edited'
);
foreach(EditableFormField::get()->filter(array("ParentID" => $parentID)) as $eff) {
if($eff->ShowInSummary) {
$summaryarray[$eff->Name] = $eff->Title ?: $eff->Name;
}
}
$config->getComponentByType('GridFieldDataColumns')->setDisplayFields($summaryarray);
/** /**
* Support for {@link https://github.com/colymba/GridFieldBulkEditingTools} * Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
*/ */
@ -440,181 +454,27 @@ class UserDefinedForm_Controller extends Page_Controller
$rules = ""; $rules = "";
$watch = array(); $watch = array();
$watchLoad = array();
if ($this->Fields()) { if ($this->Fields()) {
/** @var EditableFormField $field */
foreach ($this->Fields() as $field) { foreach ($this->Fields() as $field) {
$holderSelector = $field->getSelectorHolder(); if ($result = $field->formatDisplayRules()) {
$watch[] = $result;
// 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).closest(".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 ($watch) { if ($watch) {
foreach ($watch as $key => $values) { $rules .= $this->buildWatchJS($watch);
$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";
}
}
// Only add customScript if $default or $rules is defined // Only add customScript if $default or $rules is defined
if ($default || $rules) { if ($rules) {
Requirements::customScript(<<<JS Requirements::customScript(<<<JS
(function($) { (function($) {
$(document).ready(function() { $(document).ready(function() {
$default {$rules}
});
$rules })(jQuery);
})
})(jQuery);
JS JS
, 'UserFormsConditional'); , 'UserFormsConditional');
} }
@ -713,7 +573,7 @@ JS
foreach ($recipients as $recipient) { foreach ($recipients as $recipient) {
$email = new UserFormRecipientEmail($submittedFields); $email = new UserFormRecipientEmail($submittedFields);
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']); $mergeFields = $this->getMergeFieldsMap($emailData['Fields']);
if ($attachments) { if ($attachments) {
foreach ($attachments as $file) { foreach ($attachments as $file) {
if ($file->ID != 0) { if ($file->ID != 0) {
@ -725,7 +585,7 @@ JS
} }
} }
} }
$parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields); $parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields);
if (!$recipient->SendPlain && $recipient->emailTemplateExists()) { if (!$recipient->SendPlain && $recipient->emailTemplateExists()) {
@ -871,12 +731,55 @@ JS
Session::clear('FormProcessed'); Session::clear('FormProcessed');
} }
return $this->customise(array( $data = array(
'Content' => $this->customise(array(
'Submission' => $submission, 'Submission' => $submission,
'Link' => $referrer 'Link' => $referrer
))->renderWith('ReceivedFormSubmission'), );
$this->extend('updateReceivedFormSubmissionData', $data);
return $this->customise(array(
'Content' => $this->customise($data)->renderWith('ReceivedFormSubmission'),
'Form' => '', '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
$('.userform').on('{$events}',
"{$selectors}",
function (){
if({$operations}) {
$('{$target}').{$view}();
} else {
$('{$target}').{$opposite}();
}
});
EOS;
}
return $result;
}
} }

View File

@ -14,6 +14,8 @@ class EditableCheckbox extends EditableFormField
private static $plural_name = 'Checkboxes'; private static $plural_name = 'Checkboxes';
protected $jsEventHandler = 'click';
private static $db = array( private static $db = array(
'CheckedDefault' => 'Boolean' // from CustomSettings 'CheckedDefault' => 'Boolean' // from CustomSettings
); );
@ -61,4 +63,8 @@ class EditableCheckbox extends EditableFormField
parent::migrateSettings($data); parent::migrateSettings($data);
} }
public function isCheckBoxField() {
return true;
}
} }

View File

@ -14,6 +14,8 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField
private static $plural_name = "Checkbox Groups"; private static $plural_name = "Checkbox Groups";
protected $jsEventHandler = 'click';
public function getFormField() public function getFormField()
{ {
$field = new UserFormsCheckboxSetField($this->Name, $this->EscapedTitle, $this->getOptionsMap()); $field = new UserFormsCheckboxSetField($this->Name, $this->EscapedTitle, $this->getOptionsMap());
@ -59,4 +61,18 @@ class EditableCheckboxGroupField extends EditableMultipleOptionField
return "$(\"input[name='{$this->Name}[]']:first\")"; return "$(\"input[name='{$this->Name}[]']:first\")";
} }
} }
public function isCheckBoxField() {
return true;
}
public function getSelectorFieldOnly()
{
return "[name='{$this->Name}[]']";
}
public function isCheckBoxGroupField()
{
return true;
}
} }

View File

@ -14,6 +14,8 @@ class EditableDateField extends EditableFormField
private static $plural_name = 'Date Fields'; private static $plural_name = 'Date Fields';
private static $has_placeholder = true;
private static $db = array( private static $db = array(
'DefaultToToday' => 'Boolean' // From customsettings 'DefaultToToday' => 'Boolean' // From customsettings
); );

View File

@ -1,12 +1,15 @@
<?php <?php
/** /**
* EditableDropdown * EditableDropdown
* *
* Represents a modifiable dropdown (select) box on a form * Represents a modifiable dropdown (select) box on a form
* *
* @property bool $UseEmptyString
* @property string $EmptyString
*
* @package userforms * @package userforms
*/ */
class EditableDropdown extends EditableMultipleOptionField class EditableDropdown extends EditableMultipleOptionField
{ {
@ -14,6 +17,11 @@ class EditableDropdown extends EditableMultipleOptionField
private static $plural_name = 'Dropdowns'; private static $plural_name = 'Dropdowns';
private static $db = array(
'UseEmptyString' => 'Boolean',
'EmptyString' => 'Varchar(255)',
);
/** /**
* @return FieldList * @return FieldList
*/ */
@ -21,6 +29,18 @@ class EditableDropdown extends EditableMultipleOptionField
{ {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$fields->addFieldToTab(
'Root.Main',
CheckboxField::create('UseEmptyString')
->setTitle('Set default empty string')
);
$fields->addFieldToTab(
'Root.Main',
TextField::create('EmptyString')
->setTitle('Empty String')
);
$fields->removeByName('Default'); $fields->removeByName('Default');
return $fields; return $fields;
@ -35,6 +55,10 @@ class EditableDropdown extends EditableMultipleOptionField
->setFieldHolderTemplate('UserFormsField_holder') ->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsDropdownField'); ->setTemplate('UserFormsDropdownField');
if ($this->UseEmptyString) {
$field->setEmptyString(($this->EmptyString) ? $this->EmptyString : '');
}
// Set default // Set default
$defaultOption = $this->getDefaultOptions()->first(); $defaultOption = $this->getDefaultOptions()->first();
if ($defaultOption) { if ($defaultOption) {

23
code/model/editableformfields/EditableEmailField.php Executable file → Normal file
View File

@ -14,24 +14,7 @@ class EditableEmailField extends EditableFormField
private static $plural_name = 'Email Fields'; private static $plural_name = 'Email Fields';
private static $db = array( private static $has_placeholder = true;
'Placeholder' => 'Varchar(255)'
);
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
$fields->addFieldToTab(
'Root.Main',
TextField::create(
'Placeholder',
_t('EditableTextField.PLACEHOLDER', 'Placeholder')
)
);
});
return parent::getCMSFields();
}
public function getSetsOwnError() public function getSetsOwnError()
{ {
@ -59,9 +42,5 @@ class EditableEmailField extends EditableFormField
parent::updateFormField($field); parent::updateFormField($field);
$field->setAttribute('data-rule-email', true); $field->setAttribute('data-rule-email', true);
if ($this->Placeholder) {
$field->setAttribute('placeholder', $this->Placeholder);
}
} }
} }

View File

@ -14,6 +14,8 @@ use SilverStripe\Forms\SegmentField;
* @property int $Sort * @property int $Sort
* @property bool $Required * @property bool $Required
* @property string $CustomErrorMessage * @property string $CustomErrorMessage
* @property boolean $ShowOnLoad
* @property string $DisplayRulesConjunction
* @method UserDefinedForm Parent() Parent page * @method UserDefinedForm Parent() Parent page
* @method DataList DisplayRules() List of EditableCustomRule objects * @method DataList DisplayRules() List of EditableCustomRule objects
* @mixin Versioned * @mixin Versioned
@ -60,6 +62,13 @@ class EditableFormField extends DataObject
*/ */
public static $allowed_css = array(); public static $allowed_css = array();
/**
* Set this to true to enable placeholder field for any given class
* @config
* @var bool
*/
private static $has_placeholder = false;
/** /**
* @config * @config
* @var array * @var array
@ -84,14 +93,18 @@ class EditableFormField extends DataObject
"CustomSettings" => "Text", // @deprecated from 2.0 "CustomSettings" => "Text", // @deprecated from 2.0
"Migrated" => "Boolean", // set to true when migrated "Migrated" => "Boolean", // set to true when migrated
"ExtraClass" => "Text", // from CustomSettings "ExtraClass" => "Text", // from CustomSettings
"RightTitle" => "Varchar(255)", // from CustomSettings "RightTitle" => "Varchar(255)", // from CustomSettings
"ShowOnLoad" => "Boolean(1)", // from CustomSettings "ShowOnLoad" => "Boolean(1)", // from CustomSettings
); "ShowInSummary" => "Boolean",
"Placeholder" => "Varchar(255)",
'DisplayRulesConjunction' => 'Enum("And,Or","Or")',
);
private static $defaults = array(
'ShowOnLoad' => true, private static $defaults = array(
); 'ShowOnLoad' => true,
);
/** /**
@ -125,6 +138,22 @@ class EditableFormField extends DataObject
*/ */
protected $readonly; 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 * Set the visibility of an individual form field
* *
@ -161,6 +190,7 @@ class EditableFormField extends DataObject
_t('EditableFormField.TYPE', 'Type'), _t('EditableFormField.TYPE', 'Type'),
$this->i18n_singular_name() $this->i18n_singular_name()
), ),
CheckboxField::create('ShowInSummary', _t('EditableFormField.SHOWINSUMMARY', 'Show in summary gridfield')),
LiteralField::create( LiteralField::create(
'MergeField', 'MergeField',
_t( _t(
@ -228,6 +258,17 @@ class EditableFormField extends DataObject
$fields->addFieldsToTab('Root.DisplayRules', $displayFields); $fields->addFieldsToTab('Root.DisplayRules', $displayFields);
} }
// Placeholder
if ($this->config()->has_placeholder) {
$fields->addFieldToTab(
'Root.Main',
TextField::create(
'Placeholder',
_t('EditableFormField.PLACEHOLDER', 'Placeholder')
)
);
}
$this->extend('updateCMSFields', $fields); $this->extend('updateCMSFields', $fields);
return $fields; return $fields;
@ -243,35 +284,27 @@ class EditableFormField extends DataObject
// Check display rules // Check display rules
if ($this->Required) { if ($this->Required) {
return new FieldList( return new FieldList(
LabelField::create(_t( LabelField::create(
_t(
'EditableFormField.DISPLAY_RULES_DISABLED', 'EditableFormField.DISPLAY_RULES_DISABLED',
'Display rules are not enabled for required fields. ' . 'Display rules are not enabled for required fields. Please uncheck "Is this field Required?" under "Validation" to re-enable.'))
'Please uncheck "Is this field Required?" under "Validation" to re-enable.' ->addExtraClass('message warning'));
))->addExtraClass('message warning')
);
} }
$self = $this; $self = $this;
$allowedClasses = array_keys($this->getEditableFieldClasses(false)); $allowedClasses = array_keys($this->getEditableFieldClasses(false));
$editableColumns = new GridFieldEditableColumns(); $editableColumns = new GridFieldEditableColumns();
$editableColumns->setDisplayFields(array( $editableColumns->setDisplayFields(array(
'Display' => '',
'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) { 'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) {
return DropdownField::create( return DropdownField::create($column, '', EditableFormField::get()->filter(array(
$column,
'',
EditableFormField::get()
->filter(array(
'ParentID' => $self->ParentID, 'ParentID' => $self->ParentID,
'ClassName' => $allowedClasses 'ClassName' => $allowedClasses,
)) ))->exclude(array(
->exclude(array( 'ID' => $self->ID,
'ID' => $self->ID ))->map('ID', 'Title'));
))
->map('ID', 'Title')
);
}, },
'ConditionOption' => function ($record, $column, $grid) { 'ConditionOption' => function ($record, $column, $grid) {
$options = Config::inst()->get('EditableCustomRule', 'condition_options'); $options = Config::inst()->get('EditableCustomRule', 'condition_options');
return DropdownField::create($column, '', $options); return DropdownField::create($column, '', $options);
}, },
'FieldValue' => function ($record, $column, $grid) { 'FieldValue' => function ($record, $column, $grid) {
@ -279,7 +312,7 @@ class EditableFormField extends DataObject
}, },
'ParentID' => function ($record, $column, $grid) use ($self) { 'ParentID' => function ($record, $column, $grid) use ($self) {
return HiddenField::create($column, '', $self->ID); return HiddenField::create($column, '', $self->ID);
} },
)); ));
// Custom rules // Custom rules
@ -293,11 +326,20 @@ class EditableFormField extends DataObject
); );
return new FieldList( return new FieldList(
CheckboxField::create('ShowOnLoad') DropdownField::create('ShowOnLoad',
->setDescription(_t( _t('EditableFormField.INITIALVISIBILITY', 'Initial visibility'),
'EditableFormField.SHOWONLOAD', array(
'Initial visibility before processing these rules' 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( GridField::create(
'DisplayRules', 'DisplayRules',
_t('EditableFormField.CUSTOMRULES', 'Custom Rules'), _t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
@ -492,7 +534,7 @@ class EditableFormField extends DataObject
{ {
$this->publish($fromStage, $toStage, $createNewVersion); $this->publish($fromStage, $toStage, $createNewVersion);
$this->publishRules($fromStage, $toStage, $createNewVersion); $this->publishRules($fromStage, $toStage, $createNewVersion);
} }
/** /**
* Publish all field rules * Publish all field rules
@ -819,6 +861,16 @@ class EditableFormField extends DataObject
if ($this->ExtraClass) { if ($this->ExtraClass) {
$field->addExtraClass($this->ExtraClass); $field->addExtraClass($this->ExtraClass);
} }
// if ShowOnLoad is false hide the field
if (!$this->ShowOnLoad) {
$field->addExtraClass($this->ShowOnLoadNice());
}
// if this field has a placeholder
if ($this->Placeholder) {
$field->setAttribute('placeholder', $this->Placeholder);
}
} }
/** /**
@ -913,7 +965,17 @@ class EditableFormField extends DataObject
*/ */
public function getSelectorHolder() 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}";
} }
/** /**
@ -921,10 +983,20 @@ class EditableFormField extends DataObject
* *
* @param EditableCustomRule $rule Custom rule this selector will be used with * @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 bool $forOnLoad Set to true if this will be invoked on load
*
* @return string
*/ */
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) 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}']";
} }
@ -984,4 +1056,95 @@ class EditableFormField extends DataObject
} }
return $this->DisplayRules(); 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;
}
} }

0
code/model/editableformfields/EditableFormHeading.php Executable file → Normal file
View File

View File

@ -145,7 +145,7 @@ class EditableLiteralField extends EditableFormField
Convert::raw2htmlname($this->Name), Convert::raw2htmlname($this->Name),
Convert::raw2att($classes), Convert::raw2att($classes),
$label, $label,
$this->Content $this->dbObject('Content')->forTemplate()
) )
); );

View File

@ -33,50 +33,52 @@ class EditableMultipleOptionField extends EditableFormField
*/ */
public function getCMSFields() public function getCMSFields()
{ {
$fields = parent::getCMSFields(); $this->beforeUpdateCMSFields(function($fields) {
$editableColumns = new GridFieldEditableColumns();
$editableColumns->setDisplayFields(array(
'Title' => array(
'title' => _t('EditableMultipleOptionField.TITLE', 'Title'),
'callback' => function ($record, $column, $grid) {
return TextField::create($column);
}
),
'Value' => array(
'title' => _t('EditableMultipleOptionField.VALUE', 'Value'),
'callback' => function ($record, $column, $grid) {
return TextField::create($column);
}
),
'Default' => array(
'title' => _t('EditableMultipleOptionField.DEFAULT', 'Selected by default?'),
'callback' => function ($record, $column, $grid) {
return CheckboxField::create($column);
}
)
));
$editableColumns = new GridFieldEditableColumns(); $optionsConfig = GridFieldConfig::create()
$editableColumns->setDisplayFields(array( ->addComponents(
'Title' => array( new GridFieldToolbarHeader(),
'title' => _t('EditableMultipleOptionField.TITLE', 'Title'), new GridFieldTitleHeader(),
'callback' => function ($record, $column, $grid) { new GridFieldOrderableRows('Sort'),
return TextField::create($column); $editableColumns,
} new GridFieldButtonRow(),
), new GridFieldAddNewInlineButton(),
'Value' => array( new GridFieldDeleteAction()
'title' => _t('EditableMultipleOptionField.VALUE', 'Value'), );
'callback' => function ($record, $column, $grid) {
return TextField::create($column);
}
),
'Default' => array(
'title' => _t('EditableMultipleOptionField.DEFAULT', 'Selected by default?'),
'callback' => function ($record, $column, $grid) {
return CheckboxField::create($column);
}
)
));
$optionsConfig = GridFieldConfig::create() $optionsGrid = GridField::create(
->addComponents( 'Options',
new GridFieldToolbarHeader(), _t('EditableFormField.CUSTOMOPTIONS', 'Options'),
new GridFieldTitleHeader(), $this->Options(),
new GridFieldOrderableRows('Sort'), $optionsConfig
$editableColumns,
new GridFieldButtonRow(),
new GridFieldAddNewInlineButton(),
new GridFieldDeleteAction()
); );
$optionsGrid = GridField::create( $fields->insertAfter(new Tab('Options', _t('EditableMultipleOptionField.OPTIONSTAB', 'Options')), 'Main');
'Options', $fields->addFieldToTab('Root.Options', $optionsGrid);
_t('EditableFormField.CUSTOMOPTIONS', 'Options'), });
$this->Options(),
$optionsConfig
);
$fields->insertAfter(new Tab('Options', _t('EditableMultipleOptionField.OPTIONSTAB', 'Options')), 'Main'); $fields = parent::getCMSFields();
$fields->addFieldToTab('Root.Options', $optionsGrid);
return $fields; return $fields;
} }

View File

@ -14,10 +14,11 @@ class EditableNumericField extends EditableFormField
private static $plural_name = 'Numeric Fields'; private static $plural_name = 'Numeric Fields';
private static $has_placeholder = true;
private static $db = array( private static $db = array(
'MinValue' => 'Int', 'MinValue' => 'Int',
'MaxValue' => 'Int', 'MaxValue' => 'Int'
'Placeholder' => 'Varchar(255)'
); );
public function getSetsOwnError() public function getSetsOwnError()
@ -25,21 +26,6 @@ class EditableNumericField extends EditableFormField
return true; return true;
} }
public function getCMSFields()
{
$this->beforeUpdateCMSFields(function ($fields) {
$fields->addFieldToTab(
'Root.Main',
TextField::create(
'Placeholder',
_t('EditableTextField.PLACEHOLDER', 'Placeholder')
)
);
});
return parent::getCMSFields();
}
/** /**
* @return NumericField * @return NumericField
*/ */
@ -85,9 +71,5 @@ class EditableNumericField extends EditableFormField
if ($this->MaxValue) { if ($this->MaxValue) {
$field->setAttribute('data-rule-max', $this->MaxValue); $field->setAttribute('data-rule-max', $this->MaxValue);
} }
if ($this->Placeholder) {
$field->setAttribute('placeholder', $this->Placeholder);
}
} }
} }

View File

@ -46,4 +46,8 @@ class EditableRadioField extends EditableMultipleOptionField
$first = $forOnLoad ? ':first' : ''; $first = $forOnLoad ? ':first' : '';
return "$(\"input[name='{$this->Name}']{$first}\")"; return "$(\"input[name='{$this->Name}']{$first}\")";
} }
public function isRadioField() {
return true;
}
} }

54
code/model/editableformfields/EditableTextField.php Executable file → Normal file
View File

@ -14,11 +14,45 @@ class EditableTextField extends EditableFormField
private static $plural_name = 'Text Fields'; private static $plural_name = 'Text Fields';
private static $has_placeholder = true;
private static $autocomplete_options = array(
'off' => 'Off',
'on' => 'On',
'name' => 'Full name',
'honorific-prefix' => 'Prefix or title',
'given-name' => 'First name',
'additional-name' => 'Additional name',
'family-name' => 'Family name',
'honorific-suffix' => 'Suffix (e.g Jr.)',
'nickname' => 'Nickname',
'email' => 'Email',
'organization-title' => 'Job title',
'organization' => 'Organization',
'street-address' => 'Street address',
'address-line1' => 'Address line 1',
'address-line2' => 'Address line 2',
'address-line3' => 'Address line 3',
'address-level1' => 'Address level 1',
'address-level2' => 'Address level 2',
'address-level3' => 'Address level 3',
'address-level4' => 'Address level 4',
'country' => 'Country',
'country-name' => 'Country name',
'postal-code' => 'Postal code',
'bday' => 'Birthday',
'sex' => 'Gender identity',
'tel' => 'Telephone number',
'url' => 'Home page'
);
protected $jsEventHandler = 'keyup';
private static $db = array( private static $db = array(
'MinLength' => 'Int', 'MinLength' => 'Int',
'MaxLength' => 'Int', 'MaxLength' => 'Int',
'Rows' => 'Int(1)', 'Rows' => 'Int(1)',
'Placeholder' => 'Varchar(255)' 'Autocomplete' => 'Varchar(255)'
); );
private static $defaults = array( private static $defaults = array(
@ -41,11 +75,16 @@ class EditableTextField extends EditableFormField
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextField::create( DropdownField::create(
'Placeholder', 'Autocomplete',
_t('EditableTextField.PLACEHOLDER', 'Placeholder') _t('EditableTextField.AUTOCOMPLETE', 'Autocomplete'),
) $this->config()->get('autocomplete_options')
)->setDescription(_t(
'EditableTextField.AUTOCOMPLETE_DESCRIPTION',
'Supported browsers will attempt to populate this field automatically with the users information, use to set the value populated'
))
); );
}); });
return parent::getCMSFields(); return parent::getCMSFields();
@ -113,8 +152,9 @@ class EditableTextField extends EditableFormField
$field->setAttribute('data-rule-maxlength', intval($this->MaxLength)); $field->setAttribute('data-rule-maxlength', intval($this->MaxLength));
} }
if ($this->Placeholder) { if ($this->Autocomplete) {
$field->setAttribute('placeholder', $this->Placeholder); $field->setAttribute('autocomplete', $this->Autocomplete);
} }
} }
} }

View File

@ -131,6 +131,7 @@ class UserFormsUpgradeService
// - MinLength (EditableTextField) // - MinLength (EditableTextField)
// - MaxLength (EditableTextField) // - MaxLength (EditableTextField)
// - Rows (EditableTextField) // - Rows (EditableTextField)
// - Placeholder (EditableTextField / EditableEmailField / EditableNumericField)
$customSettings = $field->CustomSettings $customSettings = $field->CustomSettings
? unserialize($field->CustomSettings) ? unserialize($field->CustomSettings)
@ -142,6 +143,11 @@ class UserFormsUpgradeService
} }
$field->migrateSettings($customSettings); $field->migrateSettings($customSettings);
if ($field->config()->has_placeholder) {
$this->migratePlaceholder($field, $field->ClassName);
}
$field->write(); $field->write();
} }
@ -224,4 +230,62 @@ class UserFormsUpgradeService
{ {
return $this->quiet; return $this->quiet;
} }
/**
* Migrate Placeholder data from field specific table to the EditableFormField table
*
* @param EditableFormField $field
* @param string $tableName
*/
private function migratePlaceholder($field, $tableName)
{
// Migrate Placeholder setting from $tableName table to EditableFormField table
if ($field->Placeholder) {
return;
}
// Check if draft table exists
if (!DB::get_schema()->hasTable($tableName)) {
// Check if _obsolete_ draft table exists
$tableName = '_obsolete_' . $tableName;
if (!DB::get_schema()->hasTable($tableName)) {
return;
}
}
// Check if old Placeholder column exists
if (!DB::get_schema()->hasField($tableName, 'Placeholder')) {
return;
}
// Fetch existing draft Placeholder value
$query = "SELECT \"Placeholder\" FROM \"$tableName\" WHERE \"ID\" = '$field->ID'";
$draftPlaceholder = DB::query($query)->value();
if (!$draftPlaceholder) {
return;
}
// Update draft Placeholder value
DB::prepared_query(
"UPDATE \"EditableFormField\" SET \"Placeholder\" = ? WHERE \"ID\" = ?",
array($draftPlaceholder, $field->ID)
);
$livePlaceholder = $draftPlaceholder;
// Check if live table exists
$tableName = $tableName . '_Live';
if (DB::get_schema()->hasTable($tableName)) {
// Fetch existing live Placeholder value
$query = "SELECT \"Placeholder\" FROM \"$tableName\" WHERE \"ID\" = '" . $field->ID . "'";
$livePlaceholder = DB::query($query)->value();
if (!$livePlaceholder) {
$livePlaceholder = $draftPlaceholder;
}
}
// Update live Placeholder value
DB::prepared_query(
"UPDATE \"EditableFormField_Live\" SET \"Placeholder\" = ? WHERE \"ID\" = ?",
array($draftPlaceholder, $field->ID)
);
}
} }

View File

@ -37,7 +37,12 @@
}, },
"suggest": { "suggest": {
"colymba/gridfield-bulk-editing-tools": "Allows for bulk management of form submissions", "colymba/gridfield-bulk-editing-tools": "Allows for bulk management of form submissions",
"silverstripe/secureassets": "Enables files uploaded via userforms to be secured from public access" "silverstripe/secureassets": "Enables files uploaded via userforms to be secured from public access",
"silverstripe/gridfieldqueuedexport": "Export large submission as CSV through queued jobs in the background"
}, },
"extra": [] "extra": {
} "branch-alias": {
"dev-4.x-dev": "4.4.x-dev"
}
}
}

View File

@ -72,3 +72,7 @@
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
} }
.userform .field.hide {
display: none;
}

View File

@ -18,3 +18,10 @@ it then grabs the columns for the live database it will create a backup of the t
are surplus. are surplus.
To run the task login as Admin and go to to http://yoursite/dev/tasks/UserFormsColumnCleanTask To run the task login as Admin and go to to http://yoursite/dev/tasks/UserFormsColumnCleanTask
## My CSV export times out or runs out of memory
You likely have too many submissions to fit within the PHP constraints
on your server (execution time and memory). If you can't increase these limits,
consider installing the [gridfieldqueuedexport](https://github.com/silverstripe/silverstripe-gridfieldqueuedexport) module. It uses [queuedjobs](https://github.com/silverstripe-australia/silverstripe-queuedjobs) to export
submissions in the background, providing users with a progress indicator.

View File

@ -18,6 +18,7 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
"UserForms.HIDE_OPTIONS": "Skryť možnosti", "UserForms.HIDE_OPTIONS": "Skryť možnosti",
"UserForms.LEAVE_CONFIRMATION": "Máte neuložené zmeny!", "UserForms.LEAVE_CONFIRMATION": "Máte neuložené zmeny!",
"UserForms.REMOVED_OPTION": "Voľba/možnosť odstránená", "UserForms.REMOVED_OPTION": "Voľba/možnosť odstránená",
"UserForms.SHOW_OPTIONS": "Zobraziť možnosti" "UserForms.SHOW_OPTIONS": "Zobraziť možnosti",
"UserForms.ERROR_CONTAINER_HEADER": "Opravte prosím nasledujúce chyby a skúste to znova:"
}); });
} }

View File

@ -64,6 +64,7 @@ de_DE:
NOTBLANK: 'Nicht leer' NOTBLANK: 'Nicht leer'
NOTVALUE: 'Kein Wert' NOTVALUE: 'Kein Wert'
OPTIONS: Optionen OPTIONS: Optionen
PLACEHOLDER: Platzhalter
PLURALNAME: 'Editierbare Formularfelder' PLURALNAME: 'Editierbare Formularfelder'
REQUIRED: 'Pflichtfeld?' REQUIRED: 'Pflichtfeld?'
RIGHTTITLE: 'Titel rechts' RIGHTTITLE: 'Titel rechts'
@ -108,8 +109,9 @@ de_DE:
PLURALNAME: Radio-Buttons PLURALNAME: Radio-Buttons
SINGULARNAME: Radio-Button SINGULARNAME: Radio-Button
EditableTextField: EditableTextField:
AUTOCOMPLETE: 'Automatisch vervollständigen'
AUTOCOMPLETE_DESCRIPTION: 'Unterstützte Browser versuchen, dieses Feld automatisch mit den Benutzerinformationen zu füllen, verwenden, um den gefüllten Wert festzulegen'
NUMBERROWS: 'Anzahl der Zeilen' NUMBERROWS: 'Anzahl der Zeilen'
PLACEHOLDER: Platzhalter
PLURALNAME: Textfelder PLURALNAME: Textfelder
SINGULARNAME: Textfeld SINGULARNAME: Textfeld
TEXTLENGTH: Textlänge TEXTLENGTH: Textlänge

View File

@ -83,6 +83,7 @@ en:
NOTBLANK: 'Not Blank' NOTBLANK: 'Not Blank'
NOTVALUE: 'Not Value' NOTVALUE: 'Not Value'
OPTIONS: Options OPTIONS: Options
PLACEHOLDER: Placeholder
PLURALNAME: 'Editable Form Fields' PLURALNAME: 'Editable Form Fields'
REQUIRED: 'Is this field Required?' REQUIRED: 'Is this field Required?'
REQUIRED_DESCRIPTION: 'Please note that conditional fields can''t be required' REQUIRED_DESCRIPTION: 'Please note that conditional fields can''t be required'
@ -140,9 +141,10 @@ en:
PLURALNAME: 'Radio Groups' PLURALNAME: 'Radio Groups'
SINGULARNAME: 'Radio Group' SINGULARNAME: 'Radio Group'
EditableTextField: EditableTextField:
AUTOCOMPLETE: 'Autocomplete'
AUTOCOMPLETE_DESCRIPTION: 'Supported browsers will attempt to populate this field automatically with the users information, use to set the value populated'
NUMBERROWS: 'Number of rows' NUMBERROWS: 'Number of rows'
NUMBERROWS_DESCRIPTION: 'Fields with more than one row will be generated as a textarea' NUMBERROWS_DESCRIPTION: 'Fields with more than one row will be generated as a textarea'
PLACEHOLDER: Placeholder
PLURALNAME: 'Text Fields' PLURALNAME: 'Text Fields'
RANGE_TO: to RANGE_TO: to
SINGULARNAME: 'Text Field' SINGULARNAME: 'Text Field'

View File

@ -83,6 +83,7 @@ eo:
NOTBLANK: 'Ne vaka' NOTBLANK: 'Ne vaka'
NOTVALUE: 'Ne valoro' NOTVALUE: 'Ne valoro'
OPTIONS: Agordoj OPTIONS: Agordoj
PLACEHOLDER: Lokokupilo
PLURALNAME: 'Redakteblaj formularaj kampoj' PLURALNAME: 'Redakteblaj formularaj kampoj'
REQUIRED: 'Ĉu ĉi tiu kampo estas nepra?' REQUIRED: 'Ĉu ĉi tiu kampo estas nepra?'
REQUIRED_DESCRIPTION: 'Bonvolu noti ke kondiĉaj kampoj ne povas esti nepraj' REQUIRED_DESCRIPTION: 'Bonvolu noti ke kondiĉaj kampoj ne povas esti nepraj'
@ -142,7 +143,6 @@ eo:
EditableTextField: EditableTextField:
NUMBERROWS: 'Nombro da vicoj' NUMBERROWS: 'Nombro da vicoj'
NUMBERROWS_DESCRIPTION: 'Kampoj kun pli ol unu vico generiĝos kiel tekstareo' NUMBERROWS_DESCRIPTION: 'Kampoj kun pli ol unu vico generiĝos kiel tekstareo'
PLACEHOLDER: Lokokupilo
PLURALNAME: 'Tekstaj kampoj' PLURALNAME: 'Tekstaj kampoj'
RANGE_TO: al RANGE_TO: al
SINGULARNAME: 'Teksta kampo' SINGULARNAME: 'Teksta kampo'

View File

@ -106,6 +106,8 @@ es_ES:
PLURALNAME: 'Opciones editables' PLURALNAME: 'Opciones editables'
SINGULARNAME: 'Opciones editables' SINGULARNAME: 'Opciones editables'
EditableTextField: EditableTextField:
AUTOCOMPLETE: 'Autocompletar'
AUTOCOMPLETE_DESCRIPTION: 'Los navegadores compatibles intentarán rellenar este campo automáticamente con la información de los usuarios, usarla para establecer el valor poblado'
NUMBERROWS: 'Cantidad de filas' NUMBERROWS: 'Cantidad de filas'
PLURALNAME: 'Campos de texto' PLURALNAME: 'Campos de texto'
SINGULARNAME: 'Campo de texto' SINGULARNAME: 'Campo de texto'

View File

@ -83,6 +83,7 @@ fi_FI:
NOTBLANK: 'Ei ole tyhjä' NOTBLANK: 'Ei ole tyhjä'
NOTVALUE: 'Ei ole sama kuin' NOTVALUE: 'Ei ole sama kuin'
OPTIONS: Valinnat OPTIONS: Valinnat
PLACEHOLDER: Opastusteksti
PLURALNAME: 'Muokattavat lomakekentät' PLURALNAME: 'Muokattavat lomakekentät'
REQUIRED: 'Onko tämä kenttä pakollinen?' REQUIRED: 'Onko tämä kenttä pakollinen?'
REQUIRED_DESCRIPTION: 'Huomioithan, että ehdolliset kentät eivät voi olla pakollisia' REQUIRED_DESCRIPTION: 'Huomioithan, että ehdolliset kentät eivät voi olla pakollisia'
@ -142,7 +143,6 @@ fi_FI:
EditableTextField: EditableTextField:
NUMBERROWS: 'Rivien määrä' NUMBERROWS: 'Rivien määrä'
NUMBERROWS_DESCRIPTION: 'Kenttät, joissa on enemmän kuin yksi rivi, luodaan tekstialueena.' NUMBERROWS_DESCRIPTION: 'Kenttät, joissa on enemmän kuin yksi rivi, luodaan tekstialueena.'
PLACEHOLDER: Opastusteksti
PLURALNAME: Tekstikentät PLURALNAME: Tekstikentät
RANGE_TO: RANGE_TO:
SINGULARNAME: Tekstikenttä SINGULARNAME: Tekstikenttä

View File

@ -60,6 +60,9 @@ fr_FR:
DELETE: 'Remove this option' DELETE: 'Remove this option'
DRAG: 'Drag to rearrange order of options' DRAG: 'Drag to rearrange order of options'
LOCKED: 'These fields cannot be modified' LOCKED: 'These fields cannot be modified'
EditableTextField:
AUTOCOMPLETE: 'Autocomplétion'
AUTOCOMPLETE_DESCRIPTION: 'Les navigateurs pris en charge essaieront de remplir automatiquement ce champ avec les informations des utilisateurs, utilisez pour définir la valeur remplie'
Form: Form:
FIELDISREQUIRED: '%s is required' FIELDISREQUIRED: '%s is required'
SubmittedFileField: SubmittedFileField:

View File

@ -83,6 +83,7 @@ it:
NOTBLANK: 'Non vuoto' NOTBLANK: 'Non vuoto'
NOTVALUE: 'Non valore' NOTVALUE: 'Non valore'
OPTIONS: Opzioni OPTIONS: Opzioni
PLACEHOLDER: Segnaposto
PLURALNAME: 'Campi modulo modificabili' PLURALNAME: 'Campi modulo modificabili'
REQUIRED: 'Questo campo è obbligatorio?' REQUIRED: 'Questo campo è obbligatorio?'
REQUIRED_DESCRIPTION: 'Prego nota che i campi condizionali non possono essere obbligatori' REQUIRED_DESCRIPTION: 'Prego nota che i campi condizionali non possono essere obbligatori'
@ -140,9 +141,10 @@ it:
PLURALNAME: 'Gruppi di pulsanti a scelta singola' PLURALNAME: 'Gruppi di pulsanti a scelta singola'
SINGULARNAME: 'Gruppo di pulsanti a scelta singola' SINGULARNAME: 'Gruppo di pulsanti a scelta singola'
EditableTextField: EditableTextField:
AUTOCOMPLETE: 'completamento automatico'
AUTOCOMPLETE_DESCRIPTION: 'I browser supportati cercheranno di popolare automaticamente questo campo con l'informazione agli utenti, usare per impostare il valore popolata'
NUMBERROWS: 'Numero di righe' NUMBERROWS: 'Numero di righe'
NUMBERROWS_DESCRIPTION: 'Campi con più di una riga verranno generati come area testo' NUMBERROWS_DESCRIPTION: 'Campi con più di una riga verranno generati come area testo'
PLACEHOLDER: Segnaposto
PLURALNAME: 'Campi testo' PLURALNAME: 'Campi testo'
RANGE_TO: a RANGE_TO: a
SINGULARNAME: 'Campo testo' SINGULARNAME: 'Campo testo'

View File

@ -103,6 +103,8 @@ pl_PL:
PLURALNAME: 'Pola wyboru' PLURALNAME: 'Pola wyboru'
SINGULARNAME: 'Pole wyboru' SINGULARNAME: 'Pole wyboru'
EditableTextField: EditableTextField:
AUTOCOMPLETE: 'autouzupełnienie'
AUTOCOMPLETE_DESCRIPTION: 'Obsługiwane przeglądarki podejmie próbę wypełnienia tego pola automatycznie z informacji użytkowników, należy ustawić wartość zaludnionych'
NUMBERROWS: 'Liczba wierszy' NUMBERROWS: 'Liczba wierszy'
PLURALNAME: 'Pola tekstowe' PLURALNAME: 'Pola tekstowe'
SINGULARNAME: 'Pole tekstowe' SINGULARNAME: 'Pole tekstowe'

View File

@ -60,6 +60,9 @@ ru_RU:
DELETE: 'Remove this option' DELETE: 'Remove this option'
DRAG: 'Drag to rearrange order of options' DRAG: 'Drag to rearrange order of options'
LOCKED: 'These fields cannot be modified' LOCKED: 'These fields cannot be modified'
EditableTextField:
AUTOCOMPLETE: 'автозаполнения'
AUTOCOMPLETE_DESCRIPTION: 'Поддерживаемые браузеры будут пытаться автоматически заполнять это поле с информацией пользователей, используйте для установки значения населенную'
Form: Form:
FIELDISREQUIRED: '%s is required' FIELDISREQUIRED: '%s is required'
SubmittedFileField: SubmittedFileField:

View File

@ -83,6 +83,7 @@ sk:
NOTBLANK: Vyplnené NOTBLANK: Vyplnené
NOTVALUE: 'Nie zadanú hodnotu' NOTVALUE: 'Nie zadanú hodnotu'
OPTIONS: Voľba/možnosť OPTIONS: Voľba/možnosť
PLACEHOLDER: 'Zástupná/Ukážková hodnota (placeholder)'
PLURALNAME: 'Formulárové polia' PLURALNAME: 'Formulárové polia'
REQUIRED: 'Je pole povinné/vyžadované?' REQUIRED: 'Je pole povinné/vyžadované?'
REQUIRED_DESCRIPTION: 'Všimnite si prosím, že podmienené polia nemôžu byť vyžadované.' REQUIRED_DESCRIPTION: 'Všimnite si prosím, že podmienené polia nemôžu byť vyžadované.'
@ -142,7 +143,6 @@ sk:
EditableTextField: EditableTextField:
NUMBERROWS: 'Počet riadkov' NUMBERROWS: 'Počet riadkov'
NUMBERROWS_DESCRIPTION: 'Políčka s viac ako jedným riadkom sú generované ako textarea.' NUMBERROWS_DESCRIPTION: 'Políčka s viac ako jedným riadkom sú generované ako textarea.'
PLACEHOLDER: 'Zástupná/Ukážková hodnota (placeholder)'
PLURALNAME: 'Textové polia' PLURALNAME: 'Textové polia'
RANGE_TO: do RANGE_TO: do
SINGULARNAME: 'Textové pole' SINGULARNAME: 'Textové pole'

View 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']);
}
}

View 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

View File

@ -0,0 +1,42 @@
<?php
/**
* Tests the {@see EditableDropdown} class
*/
class EditableDropdownTest extends SapphireTest
{
public static $fixture_file = 'userforms/tests/EditableFormFieldTest.yml';
public function setUp()
{
parent::setUp();
}
/**
* Tests that the field sets the empty string if set
*/
public function testFormField()
{
if (!$dropdown = EditableDropdown::get()->filter('UseEmptyString', true)->first()) {
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown');
$dropdown->UseEmptyString = true;
$dropdown->EmptyString = 'My Default Empty String';
$dropdown->write();
}
$field = $dropdown->getFormField();
$this->assertEquals($field->getEmptyString(), 'My Default Empty String');
$alternateDropdown = $this->objFromFixture('EditableDropdown', 'department-dropdown');
$formField = $alternateDropdown->getFormField();
$this->assertFalse($formField->getHasEmptyDefault());
$alternateDropdown->UseEmptyString = true;
$alternateDropdown->write();
$this->assertEquals($formField->getEmptyString(), '');
}
}

View File

@ -204,4 +204,13 @@ class EditableFormFieldTest extends FunctionalTest
$this->assertEquals(10, $attributes['data-rule-minlength']); $this->assertEquals(10, $attributes['data-rule-minlength']);
$this->assertEquals(20, $attributes['data-rule-maxlength']); $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']);
}
} }

View File

@ -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: EditableCustomRule:
rule1:
Display: Show
ConditionOption: HasValue
FieldValue: NZ
ConditionField: =>EditableFormField.countryTextField
Parent: =>EditableFormField.irdNumberField
rule-1: rule-1:
Display: Hide Display: Hide
ConditionOption: HasValue ConditionOption: HasValue
FieldValue: 6 FieldValue: 6
EditableOption: EditableOption:
option-1: option-1:
Name: Option1 Name: Option1
@ -144,4 +160,4 @@ UserDefinedForm:
Title: Custom Rules Form Title: Custom Rules Form
Fields: =>EditableCheckbox.checkbox-with-rule, =>EditableTextField.basic-text-2 Fields: =>EditableCheckbox.checkbox-with-rule, =>EditableTextField.basic-text-2
empty-form: empty-form:
Title: Empty Form Title: Empty Form

View File

@ -50,6 +50,26 @@ class UserDefinedFormTest extends FunctionalTest
$this->assertTrue($fields->dataFieldByName('OnCompleteMessage') != null); $this->assertTrue($fields->dataFieldByName('OnCompleteMessage') != null);
} }
public function testGetCMSFieldsShowInSummary()
{
$this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'summary-rules-form');
$fields = $form->getCMSFields();
$this->assertInstanceOf('GridField', $fields->dataFieldByName('Submissions'));
$submissionsgrid = $fields->dataFieldByName('Submissions');
$gridFieldDataColumns = $submissionsgrid->getConfig()->getComponentByType('GridFieldDataColumns');
$summaryFields = $gridFieldDataColumns->getDisplayFields($submissionsgrid);
$this->assertContains('SummaryShow', array_keys($summaryFields), 'Summary field not showing displayed field');
$this->assertNotContains('SummaryHide', array_keys($summaryFields), 'Summary field showing displayed field');
}
public function testEmailRecipientPopup() public function testEmailRecipientPopup()
{ {
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');

View File

@ -106,6 +106,16 @@ EditableTextField:
Title: Required Text Field Title: Required Text Field
Required: true Required: true
CustomErrorMessage: 'This field is required' CustomErrorMessage: 'This field is required'
summary-show:
Name: SummaryShow
Title: Summary Text Field
ShowInSummary: true
summary-hide:
Name: SummaryHide
Title: Summary Text Field
ShowInSummary: false
EditableDropdown: EditableDropdown:
basic-dropdown: basic-dropdown:
@ -250,7 +260,12 @@ UserDefinedForm:
custom-rules-form: custom-rules-form:
Title: Custom Rules Form Title: Custom Rules Form
Fields: =>EditableFormStep.form4step1,=>EditableCheckbox.checkbox-2, =>EditableTextField.basic-text-2 Fields: =>EditableCheckbox.checkbox-2, =>EditableTextField.basic-text-2
summary-rules-form:
Title: Summary Fields Form
Fields: =>EditableTextField.summary-show, =>EditableTextField.summary-hide
empty-form: empty-form:
Title: Empty Form Title: Empty Form