public function updateDynamicListCMSFields($fields) { // Make sure the draft records are being looked at. $stage = Versioned::current_stage(); Versioned::reading_stage('Stage'); $used = EditableFormField::get()->filter(array('ClassName:PartialMatch' => 'DynamicList')); // Determine whether this dynamic list is being used anywhere. $found = array(); foreach ($used as $field) { // This information is stored using a serialised list, therefore we need to iterate through. if ($field->getSetting('ListTitle') === $this->owner->Title) { // Make sure there are no duplicates recorded. if (!isset($found[$field->ParentID]) && ($form = UserDefinedForm::get()->byID($field->ParentID))) { $found[$field->ParentID] = "<a href='{$form->CMSEditLink()}'>{$form->Title}</a>"; } } } // Display whether there were any dynamic lists found on user defined forms. if (count($found)) { $fields->removeByName('UsedOnHeader'); $fields->addFieldToTab('Root.Main', HeaderField::create('UsedOnHeader', 'Used On', 5)); } $display = count($found) ? implode('<br>', $found) : 'This dynamic list is <strong>not</strong> used.'; $fields->removeByName('UsedOn'); $fields->addFieldToTab('Root.Main', LiteralField::create('UsedOn', '<div>' . $display . '</div>')); Versioned::reading_stage($stage); }
public function php($data) { if (!parent::php($data)) { return false; } // Skip unsaved records if (empty($data['ID']) || !is_numeric($data['ID'])) { return true; } $fields = EditableFormField::get()->filter('ParentID', $data['ID'])->sort('"Sort" ASC'); // Current nesting $stack = array(); $conditionalStep = false; // Is the current step conditional? foreach ($fields as $field) { if ($field instanceof EditableFormStep) { // Page at top level, or after another page is ok if (empty($stack) || count($stack) === 1 && $stack[0] instanceof EditableFormStep) { $stack = array($field); $conditionalStep = $field->DisplayRules()->count() > 0; continue; } $this->validationError('FormFields', _t("UserFormValidator.UNEXPECTED_BREAK", "Unexpected page break '{name}' inside nested field '{group}'", array('name' => $field->CMSTitle, 'group' => end($stack)->CMSTitle)), 'error'); return false; } // Validate no pages if (empty($stack)) { $this->validationError('FormFields', _t("UserFormValidator.NO_PAGE", "Field '{name}' found before any pages", array('name' => $field->CMSTitle)), 'error'); return false; } // Nest field group if ($field instanceof EditableFieldGroup) { $stack[] = $field; continue; } // Unnest field group if ($field instanceof EditableFieldGroupEnd) { $top = end($stack); // Check that the top is a group at all if (!$top instanceof EditableFieldGroup) { $this->validationError('FormFields', _t("UserFormValidator.UNEXPECTED_GROUP_END", "'{name}' found without a matching group", array('name' => $field->CMSTitle)), 'error'); return false; } // Check that the top is the right group if ($top->EndID != $field->ID) { $this->validationError('FormFields', _t("UserFormValidator.WRONG_GROUP_END", "'{name}' found closes the wrong group '{group}'", array('name' => $field->CMSTitle, 'group' => $top->CMSTitle)), 'error'); return false; } // Unnest group array_pop($stack); } // Normal field type if ($conditionalStep && $field->Required) { $this->validationError('FormFields', _t("UserFormValidator.CONDITIONAL_REQUIRED", "Required field '{name}' cannot be placed within a conditional page", array('name' => $field->CMSTitle)), 'error'); return false; } } return true; }
public function updateCMSFields(FieldList $fields) { $formID = $this->owner->FormID != 0 ? $this->owner->FormID : Session::get('CMSMain.currentPage'); $formFields = EditableFormField::get()->filter('ParentID', (int) $formID); if ($formFields) { $source = $formFields->map('ID', 'Title'); $fields->push(DropdownField::create('ConditionFieldID', 'Only send this email if the following field', $source)->setEmptyString('Select...')); $fields->push(TextField::create('ConditionFieldValue', '...has this value')); } }
/** * Generate the javascript for the conditional field show / hiding logic. * * @return void */ public function generateConditionalJavascript() { $default = ""; $rules = ""; $watch = array(); $watchLoad = array(); if ($this->Fields()) { 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->DisplayRules() 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; } else { if ($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")'; } else { if ($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")'; } else { if ($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) { 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}) { \t{$holder} \t\t.{$view}() \t\t.trigger('userform.field.{$view}'); } else { \t{$holder} \t\t.{$opposite}() \t\t.trigger('userform.field.{$opposite}'); } EOS; } $logic = implode("\n", $logic); $rules .= $key . ".each(function() {\n\n\t\$(this).data('userformConditions', function() {\n\n\t\t{$logic}\n\n\t}); \n\n});\n"; foreach ($actions as $action) { $rules .= $key . ".{$action}(function() {\n\t\$(this).data('userformConditions').call(this);\n\n});\n"; } } } if ($watchLoad) { foreach ($watchLoad as $key => $value) { $rules .= $key . ".each(function() {\n\t\$(this).data('userformConditions').call(this);\n\n});\n"; } } // Only add customScript if $default or $rules is defined if ($default || $rules) { Requirements::customScript(<<<JS \t\t\t\t(function(\$) { \t\t\t\t\t\$(document).ready(function() { \t\t\t\t\t\t{$default} \t\t\t\t\t\t{$rules} \t\t\t\t\t}) \t\t\t\t})(jQuery); JS , 'UserFormsConditional'); } }
/** * Generate a new non-conflicting Name value * * @return string */ protected function generateName() { do { // Generate a new random name after this class $class = get_class($this); $entropy = substr(sha1(uniqid()), 0, 5); $name = "{$class}_{$entropy}"; // Check if it conflicts $exists = EditableFormField::get()->filter('Name', $name)->count() > 0; } while ($exists); return $name; }
/** * Return the html for a field option such as a * dropdown field or a radio check box field * * @return bool|html */ public function addoptionfield() { if (!SecurityToken::inst()->checkRequest($this->request)) { return $this->httpError(400); } // passed via the ajax $parent = isset($_REQUEST['Parent']) ? $_REQUEST['Parent'] : false; // work out the sort by getting the sort of the last field in the form +1 if ($parent) { $sql_parent = (int) $parent; $parentObj = EditableFormField::get()->byID($parent); $optionClass = $parentObj && $parentObj->exists() ? $parentObj->getRelationClass('Options') : 'EditableOption'; $sqlQuery = new SQLQuery(); $sqlQuery = $sqlQuery->setSelect("MAX(\"Sort\")")->setFrom("\"EditableOption\"")->setWhere("\"ParentID\" = {$sql_parent}"); $sort = $sqlQuery->execute()->value() + 1; if ($parent) { $object = Injector::inst()->create($optionClass); $object->write(); $object->ParentID = $parent; $object->Sort = $sort; $object->Name = 'option' . $object->ID; $object->write(); return $object->EditSegment(); } } return false; }
/** * Create or find an existing field with the matched specification * * @param EditableFormField $field * @param string $stage * @param string $conditionOption * @param string $display * @param string $conditionFieldName * @param string $value * @return EditableCustomRule */ protected function findOrCreateRule(EditableFormField $field, $stage, $conditionOption, $display, $conditionFieldName, $value) { // Get id of field $conditionField = $conditionFieldName ? EditableFormField::get()->filter('Name', $conditionFieldName)->first() : null; // If live, search stage record for matching one if ($stage === 'Live') { $list = Versioned::get_by_stage('EditableCustomRule', 'Stage')->filter(array('ParentID' => $field->ID, 'ConditionFieldID' => $conditionField ? $conditionField->ID : 0, 'Display' => $display, 'ConditionOption' => $conditionOption)); if ($value) { $list = $list->filter('FieldValue', $value); } else { $list = $list->where('"FieldValue" IS NULL OR "FieldValue" = \'\''); } $rule = $list->first(); if ($rule) { $rule->write(); $rule->publish("Stage", "Live"); return $rule; } } // If none found, or in stage, create new record $rule = new EditableCustomRule(); $rule->ParentID = $field->ID; $rule->ConditionFieldID = $conditionField ? $conditionField->ID : 0; $rule->Display = $display; $rule->ConditionOption = $conditionOption; $rule->FieldValue = $value; $rule->write(); return $rule; }
/** * @return void */ public function onBeforeWrite() { parent::onBeforeWrite(); if ($this->Name === 'Field') { throw new ValidationException('Field name cannot be "Field"'); } if (!$this->Sort && $this->ParentID) { $parentID = $this->ParentID; $this->Sort = EditableFormField::get()->filter('ParentID', $parentID)->max('Sort') + 1; } }