Validations.php000064400000014010150732270350007532 0ustar00 false, 'attribute' => '', 'length' => 0, 'rule' => '' ]; /** * The extracted validation rules. * * @var array */ protected $rules = []; /** * The extracted validation messages. * * @var array */ protected $messages = []; /** * The validation extractor constructor. * * @param array $formFields * @param array $formData */ public function __construct($formFields = [], $formData = []) { $this->fields = $formFields; $this->inputs = $formData; } /** * Get the extracted validation rules and messages. * * @return array */ public function get() { foreach ($this->fields as $fieldName => $field) { $this->setFieldAccessor($fieldName); $fieldValue = $this->getFieldValue(); $rules = (array) $field['rules']; $hasRequiredRule = Arr::get($rules, 'required.value'); // If the field is a repeater we'll set some settings here. $this->setRepeater($fieldName, $field); foreach ($rules as $ruleName => $rule) { if ($this->shouldNotSkipThisRule($rule, $fieldValue, $hasRequiredRule)) { $this->prepareValidations($fieldName, $ruleName, $rule); } } } return [$this->rules, $this->messages]; } /** * Set the field accessor by replacing the `[]`, `*` by `.` * so that dot notation can be used to access the inputs. * * @param string $fieldName * @return $this */ protected function setFieldAccessor($fieldName) { $this->accessor = rtrim(str_replace(['[', ']', '*'], ['.'], $fieldName), '.'); return $this; } /** * Get the field value from the form data. * * @return mixed */ protected function getFieldValue() { return Arr::get($this->inputs, $this->accessor); } /** * Set the repeater settings if the field in * iteration is indeed a repeater field. * * @param string $fieldName * @param array $field * @return $this */ protected function setRepeater($fieldName, $field) { $isRepeater = Arr::get($field, 'element') === 'input_repeat' || Arr::get($field, 'element') == 'repeater_field'; if ($isRepeater) { $attribute = Arr::get($field, 'attributes.name'); $length = isset($attribute[0]) ? count($attribute[0]) : 0; $this->repeater = [ 'status' => $isRepeater, 'attribute' => $attribute, 'length' => $length, 'rule' => rtrim($fieldName, '.*') ]; } else { $this->repeater['status'] = $isRepeater; } return $this; } /** * Determines if the iteration should skip this rule or not. * * @param array $rule * @return boolean */ protected function shouldNotSkipThisRule($rule, $fieldValue, $hasRequiredRule) { // If the rule is enabled and the field is not empty and // it does have at least one required rule then we // should validate it for other rules. Else, we // will skip this rule meaning enabling empty // submission for this field. return $rule['value'] && !($fieldValue === '' && !$hasRequiredRule); } /** * Prepare the validation extraction. * * @param string $fieldName * @param string $ruleName * @param array $rule */ protected function prepareValidations($fieldName, $ruleName, $rule) { $logic = $this->getLogic($ruleName, $rule); if ($this->repeater['status']) { for ($i = 0; $i < $this->repeater['length']; $i++) { // We need to modify the field name for repeater field. $fieldName = $this->repeater['rule'].'['.$i.']'; $this->setValidations($fieldName, $ruleName, $rule, $logic); } } else { $this->setValidations($fieldName, $ruleName, $rule, $logic); } } /** * Set the validation rules & messages * * @param string $fieldName * @param string $ruleName * @param array $rule * @param string $logic */ protected function setValidations($fieldName, $ruleName, $rule, $logic) { // if there is already a rule for this field we need to // concat current rule to it. else assign current rule. $this->rules[$fieldName] = isset($this->rules[$fieldName]) ? $this->rules[$fieldName].'|'.$logic : $logic; $this->messages[$fieldName.'.'.$ruleName] = $rule['message']; } /** * Get the logic name for the current rule. * * @param string $ruleName * @param array $rule * @return string */ protected function getLogic($ruleName, $rule) { // The file type input has rule values in an array. For // that we are taking arrays into consideration. $ruleValue = is_array($rule['value']) ? implode(',', array_filter(array_values(str_replace('|', ',', $rule['value'])))) : $rule['value']; return $ruleName.':'.$ruleValue; } } Extractor.php000064400000030441150732270350007236 0ustar00fields = $fields; $this->with = $with; $this->inputTypes = $inputTypes; } /** * The extractor initializer for getting the extracted data. * * @return array */ public function extract() { $this->looper($this->fields); return $this->result; } /** * The recursive looper method to loop each * of the fields and extract it's data. * * @param array $fields */ protected function looper($fields = []) { foreach ($fields as $field) { // If the field is a Container (collection of other fields) // then we will recursively call this function to resolve. if ($field['element'] === 'container') { foreach ($field['columns'] as $item) { $this->looper($item['fields']); } } // Now the field is supposed to be a flat field. // We can extract the desired keys as we want. else { if (in_array($field['element'], $this->inputTypes)) { $this->extractField($field); } } } } /** * The extractor initializer for getting the extracted data. * * @return array */ public function extractEssentials($formData) { $this->looperEssential($formData, $this->fields); return $this->result; } /** * The recursive looper method to loop each * of the fields and extract it's data. * * @param array $fields */ protected function looperEssential($formData, $fields = []) { foreach ($fields as $field) { $field['conditionals'] = Arr::get($field, 'settings.conditional_logics', []); $matched = ConditionAssesor::evaluate($field, $formData); if (!$matched) { continue; } // If the field is a Container (collection of other fields) // then we will recursively call this function to resolve. if ($field['element'] === 'container') { foreach ($field['columns'] as $item) { $this->looperEssential($formData, $item['fields']); } } // Now the field is supposed to be a flat field. // We can extract the desired keys as we want. else { if (in_array($field['element'], $this->inputTypes)) { $this->extractField($field); } } } } /** * Extract the form field. * * @param array $field * @return $this */ protected function extractField($field) { // Before starting the extraction we'll set the current // field and it's attribute name at first. And then we // will proceed to extract the field settings that // the developer demanded using $with initially. $this->prepareIteration($field, Arr::get($field, 'attributes.name')) ->setElement() ->setAdminLabel() ->setLabel() ->setOptions() ->setAdvancedOptions() ->setSettings() ->setRaw() ->setAttributes() ->setValidations() ->handleCustomField(); return $this; } /** * Set the field and attribute of the current iteration when * we loop through the form fields using the looper method. * * @param array $field * @param string $attribute * @return $this */ protected function prepareIteration($field, $attribute) { $this->field = $field; $this->attribute = $attribute; return $this; } /** * Set the element of the form field. * * @param array $field * @param string $attributeName * @return $this */ protected function setElement() { $this->result[$this->attribute]['element'] = $this->field['element']; return $this; } /** * Set the label of the form field. * * @return $this */ protected function setLabel() { if (in_array('label', $this->with)) { $this->result[$this->attribute]['label'] = Arr::get($this->field, 'settings.label', ''); } return $this; } /** * Set the admin label of the form field. * * @return $this */ protected function setAdminLabel() { if (in_array('admin_label', $this->with)) { $adminLabel = Arr::get($this->field, 'settings.admin_field_label') ?: Arr::get($this->field, 'settings.label') ?: Arr::get($this->field, 'element'); $this->result[$this->attribute]['admin_label'] = $adminLabel; } return $this; } /** * Set the options of the form field. * * @return $this */ protected function setOptions() { if (in_array('options', $this->with)) { $options = Arr::get($this->field, 'options', []); if(!$options) { $newOptions = Arr::get($this->field, 'settings.advanced_options', []); if( !$newOptions && Arr::get($this->field,'element') == 'multi_payment_component' && Arr::get($this->field,'attributes.type') != 'single' ) { $pricingOptions = Arr::get($this->field, 'settings.pricing_options', []); foreach ($pricingOptions as $pricingOption) { $newOptions[] = [ 'value' => $pricingOption['label'], 'label' => $pricingOption['label'] ]; } } $options = []; if($newOptions) { foreach ($newOptions as $option) { $value = sanitize_text_field($option['value']); $options[$value] = sanitize_text_field($option['label']); } } } $this->result[$this->attribute]['options'] = $options; } return $this; } /** * Set the advanced options of the form field. * * @return $this */ protected function setAdvancedOptions() { if (in_array('advanced_options', $this->with)) { $this->result[$this->attribute]['advanced_options'] = Arr::get($this->field, 'settings.advanced_options', []); } return $this; } protected function setSettings() { if (in_array('settings', $this->with)) { $this->result[$this->attribute]['settings'] = Arr::get($this->field, 'settings', []); } return $this; } /** * Set the attributes of the form field. * * @return $this */ protected function setAttributes() { if (in_array('attributes', $this->with)) { $this->result[$this->attribute]['attributes'] = Arr::get($this->field, 'attributes'); } return $this; } /** * Set the validation rules and conditions of the form field. * * @return $this */ protected function setValidations() { if (in_array('rules', $this->with)) { $this->result[$this->attribute]['rules'] = Arr::get( $this->field, 'settings.validation_rules' ); $this->handleMaxLengthValidation(); $this->result[$this->attribute]['conditionals'] = Arr::get( $this->field, 'settings.conditional_logics' ); } return $this; } protected function handleMaxLengthValidation() { $maxLength = Arr::get($this->field, 'attributes.maxlength'); $fieldHasMaxValidation = Arr::get($this->field, 'settings.validation_rules.max'); $shouldSetMaxValidation = $maxLength && !$fieldHasMaxValidation; if ($shouldSetMaxValidation) { $this->result[$this->attribute]['rules']['max'] = [ 'value' => $maxLength, "message" => Helper::getGlobalDefaultMessage('max'), ]; } return $this; } /** * Handle the child fields of the custom field. * * @return $this */ protected function handleCustomField() { // If this field is a custom field we'll assume it has it's child fields // under the `fields` key. Then we are gonna modify those child fields' // attribute `name`, `label` & `conditional_logics` properties using // the parent field. The current implementation will modify those // properties in a way so that we can use dot notation to access. $customFields = Arr::get($this->field, 'fields'); if ($customFields) { $parentAttribute = Arr::get($this->field, 'attributes.name'); $parentConditionalLogics = Arr::get($this->field, 'settings.conditional_logics', []); $isAddressOrNameField = in_array(Arr::get($this->field, 'element'), ['address', 'input_name']); $isRepeatField = Arr::get($this->field, 'element') === 'input_repeat' || Arr::get($this->field, 'element') == 'repeater_field'; foreach ($customFields as $index => $customField) { // If the current field is in fact `address` || `name` field // then we have to only keep the enabled child fields // by the user from the form editor settings. if ($isAddressOrNameField) { if (!Arr::get($customField, 'settings.visible', false)) { unset($customFields[$index]); continue; } } // Depending on whether the parent field is a repeat field or not // the modified attribute name of the child field will vary. if ($isRepeatField) { $modifiedAttribute = $parentAttribute.'['.$index.'].*'; } else { $modifiedAttribute = $parentAttribute.'['.Arr::get($customField, 'attributes.name').']'; } $modifiedLabel = $parentAttribute.'['.Arr::get($customField, 'settings.label').']'; $customField['attributes']['name'] = $modifiedAttribute; $customField['settings']['label'] = $modifiedLabel; // Now, we'll replace the `conditional_logics` property $customField['settings']['conditional_logics'] = $parentConditionalLogics; // Now that this field's properties are handled we can pass // it to the extract field method to extract it's data. $this->extractField($customField); } } return $this; } /** * Set the raw field of the form field. * * @return $this */ protected function setRaw() { if (in_array('raw', $this->with)) { $this->result[$this->attribute]['raw'] = $this->field; } return $this; } } Form.php000064400000030026150732270350006165 0ustar00form = $form; $this->setInputTypes(); } /** * Set input types of the form. * * @param array $types * @return \FluentForm\App\Services\Parser\Form $this */ public function setInputTypes($types = []) { // If the $types is empty we'll use the default input types. $types = $types ?: [ 'input_text', 'input_name', 'textarea', 'select', 'input_radio', 'input_checkbox', 'input_email', 'input_url', 'input_password', 'input_file', 'input_image', 'input_date', 'select_country', 'input_number', 'input_repeat', 'address', 'terms_and_condition', 'input_hidden', 'ratings', 'net_promoter', 'tabular_grid', 'gdpr_agreement', 'taxonomy' ]; $types = apply_filters_deprecated( 'fluentform_form_input_types', [ $types ], FLUENTFORM_FRAMEWORK_UPGRADE, 'fluentform/form_input_types', 'Use fluentform/form_input_types instead of fluentform_form_input_types' ); // Firing an event so that others can hook into it and add other input types. $this->inputTypes = apply_filters('fluentform/form_input_types', $types); return $this; } /** * Get form fields. * * @param boolean $asArray * @return array */ public function getFields($asArray = false) { $fields = json_decode($this->form->form_fields, $asArray); $default = $asArray ? [] : null; return Arr::get((array)$fields, 'fields', $default); } /** * Get flatten form inputs. Flatten implies that all * of the form fields will be in a simple array. * * @param array $with * @return array */ public function getInputs($with = []) { // If the form is already parsed we'll return it. Otherwise, // we'll parse the form and return the data after saving it. if (!$this->parsed) { $fields = $this->getFields(true); $with = $with ?: ['admin_label', 'element', 'options', 'attributes', 'raw']; $this->parsed = (new Extractor($fields, $with, $this->inputTypes))->extract(); } return $this->parsed; } /** * Get the inputs just as they setup in the form editor. * e.g. `names` as `names` not with the child fields. * * @param array $with * @return array */ public function getEntryInputs($with = ['admin_label']) { $inputs = $this->getInputs($with); // The inputs that has `[]` in their keys are custom fields // & for the purpose of this scenario we'll remove those. foreach ($inputs as $key => $value) { if (Str::contains($key, '[')) { unset($inputs[$key]); } } return $inputs; } /** * Get the flatten inputs as the result of the `getInputs` * method but replace the keys those have `[]` with `.` * And also remove the repeat fields' child fields. * * @param array $with * @param array */ public function getShortCodeInputs($with = ['admin_label']) { $inputs = $this->getInputs($with); $result = []; // For the purpose of this scenario we'll rename // the keys that have `[]` in 'em to `.` and // remove the keys that have `*` in 'em. foreach ($inputs as $key => $value) { if (Str::contains($key, '*')) { unset($inputs[$key]); } else { $key = str_replace(['[', ']'], ['.'], $key); $result[$key] = $value; } } return $result; } /** * Get admin labels of the form fields. * * @param array $fields * @return array */ public function getAdminLabels($fields = []) { $fields = $fields ?: $this->getInputs(['admin_label']); $labels = []; foreach ($fields as $key => $field) { $labels[$key] = Arr::get($field, 'admin_label'); } return $labels; } /** * Get admin labels of the form fields. * * @param array $inputs * @param array $fields * @return array */ public function getValidations($inputs, $fields = []) { // If the form validations are already parsed we'll return it. // Otherwise, we'll parse the form validation and return // the data after saving it to the validations array. if (!$this->validations) { $fields = $fields ?: $this->getInputs(['rules']); $this->validations = (new Validations($fields, $inputs))->get(); } return $this->validations; } /** * Get an element by it's name. * * @param string|array $name * @param array $with * @return array */ public function getElement($name, $with = []) { $this->inputTypes = (array) $name; return $this->getInputs($with); } /** * Determine whether the form has an element. * * @param string $name * @return bool */ public function hasElement($name) { $elements = $this->getElement($name, ['element']); foreach ($elements as $item) { if ($item['element'] === $name) { return true; } } return false; } /** * Determine whether the form has any required fields. * * @param array $fields * @return bool */ public function hasRequiredFields($fields = []) { // $fields can be user provided when called this method or, // the current object could have already parsed fields or, // we should parse the form and use the processed result. $fields = $fields ?: $this->parsed ?: $this->getInputs(['rules']); $exist = false; foreach ($fields as $field) { $exist = Arr::get($field, 'rules.required.value'); if ($exist) { break; } } return (boolean)$exist; } /** * Get Payment Related Fields * * @param array $with array * @return array */ public function getPaymentFields($with = ['element']) { $fields = $this->getInputs($with); $data = [ 'custom_payment_component', 'multi_payment_component', 'payment_method', 'item_quantity_component', 'rangeslider', 'payment_coupon', 'subscription_payment_component', ]; $data = apply_filters_deprecated('fluentform_form_payment_fields', [ $data ], FLUENTFORM_FRAMEWORK_UPGRADE, 'fluentform/form_payment_fields', 'Use fluentform/form_payment_fields instead of fluentform_form_payment_fields' ); $paymentElements = apply_filters('fluentform/form_payment_fields', $data); return array_filter($fields, function ($field) use ($paymentElements) { return in_array($field['element'], $paymentElements); }); } /** * Get Payment Input Fields * * @return array */ public function getPaymentInputFields($with = ['element']) { $fields = $this->getInputs($with); $data = [ 'custom_payment_component', 'multi_payment_component' ]; $data = apply_filters_deprecated( 'fluentform_form_payment_inputs', [ $data ], FLUENTFORM_FRAMEWORK_UPGRADE, 'fluentform/form_payment_inputs', 'Use fluentform/form_payment_inputs instead of fluentform_form_payment_inputs' ); $paymentElements = apply_filters('fluentform/form_payment_inputs', $data); return array_filter($fields, function ($field) use ($paymentElements) { return in_array($field['element'], $paymentElements); }); } /** * Determine whether the form has payment elements * * @return bool */ public function hasPaymentFields() { $fields = $this->getInputs(['element']); $data = [ 'custom_payment_component', 'multi_payment_component', 'payment_method', 'item_quantity_component', 'payment_coupon', 'subscription_payment_component' ]; $data = apply_filters_deprecated( 'fluentform_form_payment_fields', [ $data ], FLUENTFORM_FRAMEWORK_UPGRADE, 'fluentform/form_payment_fields', 'Use fluentform/form_payment_fields instead of fluentform_form_payment_fields' ); $paymentElements = apply_filters('fluentform/form_payment_fields', $data); foreach ($fields as $field) { if (in_array($field['element'], $paymentElements)) { return true; } } return false; } /** * Get an specific field for an element type. * * @param $element * @param $attribute * @param array $with * @return array|null */ public function getField($element, $attribute, $with = []) { $element = $this->getElement($element, $with); return array_intersect_key($element, array_flip((array)$attribute)); } /** * Get Payment Input Fields * * @return array */ public function getAttachmentInputFields($with = ['element']) { $fields = $this->getInputs($with); $paymentElements = [ 'input_file', 'input_image', 'featured_image', 'signature' ]; return array_filter($fields, function ($field) use ($paymentElements) { return in_array($field['element'], $paymentElements); }); } /** * Get Any Field Type * @return array */ public function getInputsByElementTypes($types, $with = ['element']) { $fields = $this->getInputs($with); return array_filter($fields, function ($field) use ($types) { return in_array($field['element'], $types); }); } /** * Get Address Fields * * @return array */ public function getAddressFields($with = ['admin_label', 'attributes']) { $fields = $this->getInputs($with); $addressElements = [ 'address' ]; return array_filter($fields, function ($field) use ($addressElements) { return in_array($field['element'], $addressElements); }); } public function getEssentialInputs($formData, $with = []) { // If the form is already parsed we'll return it. Otherwise, // we'll parse the form and return the data after saving it. if (!$this->essentials) { $fields = $this->getFields(true); $with = $with ?: ['rules', 'raw']; $this->essentials = (new Extractor($fields, $with, $this->inputTypes))->extractEssentials($formData); } return $this->essentials; } }