app = $app;
$this->containerCounter = 1;
}
/**
* Render the form
* @param \StdClass $form [Form entry from database]
* @return mixed
*/
public function build($form, $extraCssClass = '', $instanceCssClass = '', $atts = [])
{
$this->form = $form;
$hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper'];
$labelPlacement = $form->settings['layout']['labelPlacement'];
$formClass = "frm-fluent-form fluent_form_{$form->id} ff-el-form-{$labelPlacement}";
if ($extraCssClass) $formClass .= " {$extraCssClass}";
if ($hasStepWrapper) {
$formClass .= ' ff-form-has-steps';
}
if ($extraFormClass = Helper::formExtraCssClass($form)) {
$formClass .= ' ' . $extraFormClass;
}
$formBody = $this->buildFormBody($form);
if(strpos($formBody, '{dynamic.')) {
$formClass .= ' ff_has_dynamic_smartcode';
wp_enqueue_script('fluentform-advanced');
}
$formClass = apply_filters('fluentform_form_class', $formClass, $form);
if ($form->has_payment) {
$formClass .= ' fluentform_has_payment';
}
$formAttributes = apply_filters('fluent_form_html_attributes', [
'data-form_id' => $form->id,
'id' => 'fluentform_'.$form->id,
'class' => $formClass,
'data-form_instance' => $instanceCssClass,
'method' => 'POST'
], $form);
$formAtts = $this->buildAttributes($formAttributes);
ob_start();
$wrapperClasses = trim('fluentform fluentform_wrapper_'.$form->id.' '.ArrayHelper::get($atts, 'css_classes'));
echo "
";
do_action('fluentform_before_form_render', $form);
echo "
";
do_action('fluentform_after_form_render', $form);
return ob_get_clean();
}
public function buildFormBody($form)
{
$hasStepWrapper = isset($form->fields['stepsWrapper']) && $form->fields['stepsWrapper'];
ob_start();
$stepCounter = 1;
foreach ($form->fields['fields'] as $item) {
if ($hasStepWrapper && $item['element'] == 'form_step') {
$stepCounter++;
}
$this->setUniqueIdentifier($item);
$item = apply_filters('fluentform_before_render_item', $item, $form);
do_action('fluentform_render_item_' . $item['element'], $item, $form);
$this->extractValidationRules($item);
$this->extractConditionalLogic($item);
}
if ($hasStepWrapper) {
do_action('fluentform_render_item_step_end', $form->fields['stepsWrapper']['stepEnd'], $form);
} else {
do_action('fluentform_render_item_submit_button', $form->fields['submitButton'], $form);
}
$content = ob_get_clean();
if ($hasStepWrapper) {
$startElement = $form->fields['stepsWrapper']['stepStart'];
$steps = ArrayHelper::get($startElement, 'settings.step_titles');
// check if $stepCounter == count()
if ($stepCounter > count($steps)) {
$fillCount = $stepCounter - count($steps);
foreach (range(1, $fillCount) as $item) {
$steps[] = '';
}
$startElement['settings']['step_titles'] = $steps;
}
$this->setUniqueIdentifier($startElement);
ob_start();
do_action('fluentform_render_item_step_start', $startElement, $form);
$stepStatrt = ob_get_clean();
$content = $stepStatrt.$content;
}
return $content;
}
/**
* Set unique name/data-name for an element
* @param array &$item
* @return void
*/
protected function setUniqueIdentifier(&$item)
{
if (isset($item['columns'])) {
$item['attributes']['data-name'] = 'ff_cn_id_'.$this->containerCounter;
$this->containerCounter++;
foreach ($item['columns'] as &$column) {
foreach ($column['fields'] as &$field) {
$this->setUniqueIdentifier($field);
}
}
} else {
if (!isset($item['attributes']['name'])) {
if($this->form) {
if(empty($this->form->attr_name_index)) {
$this->form->attr_name_index = 1;
} else {
$this->form->attr_name_index += 1;
}
$uniqueId = $this->form->id.'_'.$this->form->attr_name_index;
} else {
$uniqueId = uniqid(rand(), true);
}
$item['attributes']['name'] = $item['element'] . '-' . $uniqueId;
}
$item['attributes']['data-name'] = $item['attributes']['name'];
$this->fieldLists[] = $item['element'];
}
}
/**
* Recursively extract validation rules from a given element
* @param array $item
* @return void
*/
protected function extractValidationRules($item)
{
if (isset($item['columns'])) {
foreach ($item['columns'] as $column) {
foreach ($column['fields'] as $field) {
$this->extractValidationRules($field);
}
}
} elseif (isset($item['fields'])) {
$rootName = $item['attributes']['name'];
foreach ($item['fields'] as $key => $innerItem) {
if ($item['element'] == 'address' || $item['element'] == 'input_name') {
$itemName = $innerItem['attributes']['name'];
$innerItem['attributes']['name'] = $rootName . '[' . $itemName . ']';
} else {
if ($item['element'] == 'input_repeat' || $item['element'] == 'repeater_field') {
if(empty($innerItem['settings']['validation_rules']['email'])) {
unset($innerItem['settings']['validation_rules']['email']);
}
$innerItem['attributes']['name'] = $rootName . '[' . $key . ']';
} else {
$innerItem['attributes']['name'] = $rootName;
}
}
$this->extractValidationRule($innerItem);
}
} elseif ($item['element'] == 'tabular_grid') {
$gridName = $item['attributes']['name'];
$gridRows = $item['settings']['grid_rows'];
$gridType = $item['settings']['tabular_field_type'];
foreach ($gridRows as $rowKey => $rowValue) {
if ($gridType == 'radio') {
$item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
$this->extractValidationRule($item);
} else {
$item['attributes']['name'] = $gridName . '[' . $rowKey . ']';
$this->extractValidationRule($item);
}
}
} elseif ($item['element'] == 'chained_select') {
$chainedSelectName = $item['attributes']['name'];
foreach ($item['settings']['data_source']['headers'] as $select) {
$item['attributes']['name'] = $chainedSelectName . '[' . $select . ']';
$this->extractValidationRule($item);
}
} else {
$this->extractValidationRule($item);
}
}
/**
* Extract validation rules from a given element
* @param array $item
* @return void
*/
protected function extractValidationRule($item)
{
if (isset($item['settings']['validation_rules'])) {
$rules = $item['settings']['validation_rules'];
foreach ($rules as $ruleName => $rule) {
if(isset($rule['message'])) {
$rules[$ruleName]['message'] = apply_filters('fluentform_validation_message_'.$ruleName, $rule['message'], $item);
$rules[$ruleName]['message'] = apply_filters('fluentform_validation_message_'.$item['element'].'_'.$ruleName, $rule['message'], $item);
}
}
$rules = apply_filters('fluentform_item_rules_' . $item['element'], $rules, $item);
$this->validationRules[ $item['attributes']['name'] ] = $rules;
}
}
/**
* Extract conditipnal logic from a given element
* @param array $item
* @return void
*/
protected function extractConditionalLogic($item)
{
// If container like element, then recurse
if (isset($item['columns'])) {
$containerConditions = false;
if (isset($item['settings']['conditional_logics'])) {
$conditionals = $item['settings']['conditional_logics'];
if (isset($conditionals['status'])) {
if ($conditionals['status'] && $conditionals['conditions']) {
$containerConditions = $item['settings']['conditional_logics'];
$this->conditions[$item['attributes']['data-name']] = $containerConditions;
}
}
}
foreach ($item['columns'] as $column) {
foreach ($column['fields'] as $field) {
if($containerConditions) {
$field['container_conditions'] = $containerConditions;
}
$this->extractConditionalLogic($field);
}
}
} elseif (isset($item['settings']['conditional_logics'])) {
$conditionals = $item['settings']['conditional_logics'];
if (isset($conditionals['status'])) {
if ($conditionals['status'] && $conditionals['conditions']) {
$this->conditions[$item['attributes']['data-name']] = $item['settings']['conditional_logics'];
}
}
if(isset($item['container_conditions'])) {
if(!isset($this->conditions[$item['attributes']['data-name']])) {
$this->conditions[$item['attributes']['data-name']] = [
'conditions' => [],
'status' => false,
'type' => 'any'
];
}
$this->conditions[$item['attributes']['data-name']]['container_condition'] = $item['container_conditions'];
}
}
}
/**
* Build attributes for any html element
* @param array $attributes
* @return string [Compiled key='value' attributes]
*/
protected function buildAttributes($attributes, $form = null)
{
$atts = '';
foreach ($attributes as $key => $value) {
if ($value || $value === 0 || $value === '0') {
$value = htmlspecialchars($value);
$atts .= $key.'="'.$value.'" ';
}
}
return $atts;
}
}