← All changes
|
app/Services/FluentConversational/Classes/Converter/Converter.php
+80
-5
6.2.6
→
trunk
View file →
| @@ -6,8 +6,9 @@ | ||
| 6 | 6 | |
| 7 | 7 | use FluentForm\App\Helpers\Helper; |
| 8 | 8 | use FluentForm\App\Modules\Payments\PaymentHelper; |
| 9 | 9 | use FluentForm\App\Modules\Component\Component; |
| 10 | +use FluentForm\App\Services\FormBuilder\AutocompleteTokens; | |
| 10 | 11 | use FluentForm\App\Services\FormBuilder\RatingIcon; |
| 11 | 12 | use FluentForm\App\Services\FormBuilder\Components\DateTime; |
| 12 | 13 | use FluentForm\App\Modules\Form\FormFieldsParser; |
| 13 | 14 | use FluentForm\Framework\Helpers\ArrayHelper; |
| @@ -201,8 +202,16 @@ | ||
| 201 | 202 | |
| 202 | 203 | if (!$hasSaveAndResume) { |
| 203 | 204 | $item['attributes']['value'] = self::setDefaultValue(ArrayHelper::get($item, 'attributes.value', ''), $item, $form);; |
| 204 | 205 | } |
| 206 | + // Per-sub-field token, resolved exactly as the classic renderer | |
| 207 | + // does, so both renderers agree on the same saved form. | |
| 208 | + $item['attributes']['autocomplete'] = ArrayHelper::get(AutocompleteTokens::normalizeAttributes(['autocomplete' => AutocompleteTokens::forSubField( | |
| 209 | + ArrayHelper::get($item, 'attributes.name'), | |
| 210 | + ArrayHelper::get($field, 'attributes.autocomplete'), | |
| 211 | + ArrayHelper::get($item, 'attributes.autocomplete') | |
| 212 | + )]), 'autocomplete'); | |
| 213 | + | |
| 205 | 214 | $question['fields'][] = wp_parse_args($itemQuestion, $item); |
| 206 | 215 | } |
| 207 | 216 | } |
| 208 | 217 | } elseif ('input_name' === $field['element']) { |
| @@ -232,8 +241,16 @@ | ||
| 232 | 241 | } |
| 233 | 242 | |
| 234 | 243 | $item['attributes']['value'] = self::getComponent()->replaceEditorSmartCodes(ArrayHelper::get($item, 'attributes.value'), $form); |
| 235 | 244 | |
| 245 | + // Per-sub-field token, resolved exactly as the classic renderer | |
| 246 | + // does, so both renderers agree on the same saved form. | |
| 247 | + $item['attributes']['autocomplete'] = ArrayHelper::get(AutocompleteTokens::normalizeAttributes(['autocomplete' => AutocompleteTokens::forSubField( | |
| 248 | + ArrayHelper::get($item, 'attributes.name'), | |
| 249 | + ArrayHelper::get($field, 'attributes.autocomplete'), | |
| 250 | + ArrayHelper::get($item, 'attributes.autocomplete') | |
| 251 | + )]), 'autocomplete'); | |
| 252 | + | |
| 236 | 253 | $question['fields'][] = wp_parse_args($itemQuestion, $item); |
| 237 | 254 | } |
| 238 | 255 | } |
| 239 | 256 | } elseif ('input_text' === $field['element']) { |
| @@ -257,10 +274,17 @@ | ||
| 257 | 274 | |
| 258 | 275 | if ($isMultiple) { |
| 259 | 276 | $question['multiple'] = true; |
| 260 | 277 | $question['placeholder'] = self::getComponent()->replaceEditorSmartCodes(ArrayHelper::get($field, 'attributes.placeholder', false), $form); |
| 261 | - $question['max_selection'] = ArrayHelper::get($field, 'settings.max_selection'); | |
| 262 | - $question['max_selection'] = $question['max_selection'] ? intval($question['max_selection']) : 0; | |
| 278 | + $maxSelection = Helper::resolveMaxSelection($field); | |
| 279 | + $question['max_selection'] = $maxSelection ? intval($maxSelection) : 0; | |
| 280 | + | |
| 281 | + // Resolved here so a form on the legacy setting — which has no | |
| 282 | + // rule to read a message from — still has wording. | |
| 283 | + $question['max_selection_message'] = Helper::getSelectionLimitMessage( | |
| 284 | + ArrayHelper::get($field, 'settings.validation_rules', []), | |
| 285 | + 'max_selection' | |
| 286 | + ); | |
| 263 | 287 | } |
| 264 | 288 | } elseif ('select_country' === $field['element']) { |
| 265 | 289 | $countryComponent = new \FluentForm\App\Services\FormBuilder\Components\SelectCountry(); |
| 266 | 290 | $field = $countryComponent->loadCountries($field); |
| @@ -288,9 +312,9 @@ | ||
| 288 | 312 | $question['searchable'] = ArrayHelper::get($field, 'settings.enable_select_2'); |
| 289 | 313 | } elseif ('dynamic_field' === $field['element']) { |
| 290 | 314 | $dynamicFetchValue = 'yes' == ArrayHelper::get($field, 'settings.dynamic_fetch'); |
| 291 | 315 | if ($dynamicFetchValue) { |
| 292 | - $field = apply_filters('fluentform/dynamic_field_re_fetch_result_and_resolve_value', $field); | |
| 316 | + $field = apply_filters('fluentform/dynamic_field_re_fetch_result_and_resolve_value', $field, $form->id); | |
| 293 | 317 | $question['answer'] = self::getComponent()->replaceEditorSmartCodes(ArrayHelper::get($field, 'attributes.value'), $form); |
| 294 | 318 | } |
| 295 | 319 | $type = ArrayHelper::get($field, 'settings.field_type', 'select'); |
| 296 | 320 | if (in_array($type, ['checkbox', 'radio'])) { |
| @@ -728,9 +752,9 @@ | ||
| 728 | 752 | } |
| 729 | 753 | } |
| 730 | 754 | } elseif ('payment_summary_component' === $field['element']) { |
| 731 | 755 | $question['title'] = self::getComponent()->replaceEditorSmartCodes(__('Payment Summary', 'fluentform'), $form); |
| 732 | - $question['emptyText'] = $field['settings']['cart_empty_text']; | |
| 756 | + $question['emptyText'] = fluentform_sanitize_html(ArrayHelper::get($field, 'settings.cart_empty_text', '')); | |
| 733 | 757 | } elseif ('net_promoter_score' === $field['element']) { |
| 734 | 758 | if (!ArrayHelper::exists($question, 'answer')) { |
| 735 | 759 | $question['answer'] = (int) $field['attributes']['value']; |
| 736 | 760 | } |
| @@ -871,9 +895,9 @@ | ||
| 871 | 895 | |
| 872 | 896 | public static function convertExistingForm($form) |
| 873 | 897 | { |
| 874 | 898 | $formFields = json_decode($form->form_fields, true); |
| 875 | - $fields = $formFields['fields']; | |
| 899 | + $fields = static::flattenLayoutContainers(ArrayHelper::get($formFields, 'fields', [])); | |
| 876 | 900 | $formattedFields = []; |
| 877 | 901 | |
| 878 | 902 | if (is_array($fields) && ! empty($fields)) { |
| 879 | 903 | foreach ($fields as $field) { |
| @@ -900,8 +924,58 @@ | ||
| 900 | 924 | $formFields['fields'] = $formattedFields; |
| 901 | 925 | |
| 902 | 926 | return json_encode($formFields); |
| 903 | 927 | } |
| 928 | + | |
| 929 | + /** | |
| 930 | + * Lift fields out of layout containers so they survive conversion. | |
| 931 | + * | |
| 932 | + * A conversational form is a flat sequence of questions, so `container` has | |
| 933 | + * no equivalent and is not in fieldTypes(). Dropping it wholesale used to | |
| 934 | + * take every input nested inside it as well — silent, irreversible data loss | |
| 935 | + * on a one-click action, and column layouts are common. | |
| 936 | + * | |
| 937 | + * Only `columns` is unwrapped. Composite fields (input_name, address) keep | |
| 938 | + * their sub-inputs under `fields` and have their own conversational type, so | |
| 939 | + * they must stay whole. Nested containers are handled recursively. | |
| 940 | + * | |
| 941 | + * @param array $fields | |
| 942 | + * @return array | |
| 943 | + */ | |
| 944 | + protected static function flattenLayoutContainers($fields) | |
| 945 | + { | |
| 946 | + if (! is_array($fields)) { | |
| 947 | + return []; | |
| 948 | + } | |
| 949 | + | |
| 950 | + $flattened = []; | |
| 951 | + | |
| 952 | + foreach ($fields as $field) { | |
| 953 | + if (! is_array($field)) { | |
| 954 | + continue; | |
| 955 | + } | |
| 956 | + | |
| 957 | + $columns = ArrayHelper::get($field, 'columns'); | |
| 958 | + | |
| 959 | + if ('container' !== ArrayHelper::get($field, 'element') || ! is_array($columns)) { | |
| 960 | + $flattened[] = $field; | |
| 961 | + | |
| 962 | + continue; | |
| 963 | + } | |
| 964 | + | |
| 965 | + foreach ($columns as $column) { | |
| 966 | + $columnFields = static::flattenLayoutContainers( | |
| 967 | + ArrayHelper::get($column, 'fields', []) | |
| 968 | + ); | |
| 969 | + | |
| 970 | + foreach ($columnFields as $columnField) { | |
| 971 | + $flattened[] = $columnField; | |
| 972 | + } | |
| 973 | + } | |
| 974 | + } | |
| 975 | + | |
| 976 | + return $flattened; | |
| 977 | + } | |
| 904 | 978 | |
| 905 | 979 | private static function buildBaseQuestion($field, $validationsRules, $form) |
| 906 | 980 | { |
| 907 | 981 | return [ |
| @@ -910,8 +984,9 @@ | ||
| 910 | 984 | 'title' => self::getComponent()->replaceEditorSmartCodes(ArrayHelper::get($field, 'settings.label'), $form), |
| 911 | 985 | 'type' => ArrayHelper::get(static::fieldTypes(), $field['element']), |
| 912 | 986 | 'ff_input_type' => ArrayHelper::get($field, 'element'), |
| 913 | 987 | 'container_class' => ArrayHelper::get($field, 'settings.container_class'), |
| 988 | + 'autocomplete' => ArrayHelper::get(AutocompleteTokens::normalizeAttributes((array) ArrayHelper::get($field, 'attributes', [])), 'autocomplete'), | |
| 914 | 989 | 'placeholder' => self::getComponent()->replaceEditorSmartCodes(ArrayHelper::get($field, 'attributes.placeholder'), $form), |
| 915 | 990 | 'maxLength' => ArrayHelper::get($field, 'attributes.maxlength'), |
| 916 | 991 | 'required' => ArrayHelper::get($validationsRules, 'required.value'), |
| 917 | 992 | 'requiredMsg' => ArrayHelper::get($validationsRules, 'required.message'), |