PluginProbe
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder / 1.2.4
Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder v1.2.4
6.2.13 6.2.12 6.2.10 6.2.11 6.2.9 6.2.8 6.2.7 6.2.6 6.2.5 6.2.4 6.2.3 6.2.2 3.6.22 3.6.31 3.6.40 3.6.41 3.6.42 3.6.50 3.6.51 3.6.60 3.6.61 3.6.62 3.6.64 3.6.65 3.6.66 All 195 releases
fluentform / app / Modules / Form / FormHandler.php

FormHandler.php in Fluent Forms – Customizable Contact Forms, Survey, Quiz, & Conversational Form Builder 1.2.4, at app/Modules/Form/FormHandler.php

384 lines 11.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentForm\App\Modules\Form;
4
5 use FluentForm\App\Services\FormParser;
6 use FluentForm\App\Services\Browser\Browser;
7 use FluentForm\Framework\Helpers\ArrayHelper;
8 use FluentForm\App\Modules\ReCaptcha\ReCaptcha;
9 use FluentForm\Framework\Foundation\Application;
10 use FluentForm\App\Services\FormBuilder\MessageShortCodeParser;
11
12 class FormHandler
13 {
14 /**
15 * @var \FluentForm\Framework\Foundation\Application
16 */
17 protected $app;
18
19 /**
20 * @var \FluentForm\Framework\Request\Request
21 */
22 protected $request;
23
24 /**
25 * @var \FluentForm\App\Services\FormParser
26 */
27 protected $parser;
28
29 /**
30 * @var array $formData
31 */
32 protected $formData;
33
34 /**
35 * Form Handler constructor.
36 *
37 * @param \FluentForm\Framework\Foundation\Application $app
38 */
39 public function __construct(Application $app)
40 {
41 $this->app = $app;
42 $this->request = $app->request;
43 }
44
45 /**
46 * Find the form using it's ID.
47 *
48 * @param $formId
49 *
50 * @return null|\stdClass
51 */
52 public function findForm($formId)
53 {
54 return wpFluent()->table('fluentform_forms')->find($formId);
55 }
56
57 /**
58 * Handle form submition
59 *
60 * @return \Exception
61 */
62 public function onSubmit()
63 {
64 // Parse the url encoded data from the request object.
65 parse_str($this->app->request->get('data'), $data);
66
67 // Merge it back again to the request object.
68 $this->app->request->merge(['data' => $data]);
69
70 $formId = intval($this->app->request->get('form_id'));
71
72 $form = $this->findForm($formId);
73
74 // Parse the form and get the flat inputs with validations.
75 $this->parser = (new FormParser($form));
76 $fields = $this->parser->getInputs(['rules']);
77
78 // Sanitize the data properly.
79 $this->formData = fluentFormSanitizer($data, null, $fields);
80
81 // Now validate the data using the previous validations.
82 $this->validate($form, $fields);
83
84 // Prepare the data to be inserted to the DB.
85 $insertData = $this->prepareInsertData($formId);
86
87 $insertId = wpFluent()->table('fluentform_submissions')->insert($insertData);
88
89 try {
90 $this->app->doAction('fluentform_submission_inserted', $insertId, $this->formData, $form);
91 } catch (\Exception $e) {
92 if (defined('WP_DEBUG') && WP_DEBUG) {
93 return $e;
94 }
95 }
96
97 $this->sendResponse($insertId, $form);
98 }
99
100 /**
101 * Prepare response and do actions/filters
102 * and send the response to the client.
103 *
104 * @param int $insertId
105 * @param \StdClass $form
106 *
107 * @return void
108 */
109 private function sendResponse($insertId, $form)
110 {
111 $formSettings = wpFluent()->table('fluentform_form_meta')
112 ->where('form_id', $form->id)
113 ->where('meta_key', 'formSettings')
114 ->first();
115
116 $formSettings = json_decode($formSettings->value, true);
117
118 $confirmation = $formSettings['confirmation'];
119
120 $message = MessageShortCodeParser::parseMessageShortCode(
121 array(array('message' => $confirmation['messageToShow'])),
122 $insertId,
123 $this->formData,
124 $form,
125 false
126 );
127
128 if ($confirmation['redirectTo'] == 'samePage') {
129 $returnData = array(
130 'message' => $message[0]['message'],
131 'formBehavior' => $confirmation['samePageFormBehavior'],
132 );
133 } else {
134 $redirectUrl = $confirmation['customUrl'];
135 if ($confirmation['redirectTo'] == 'customPage') {
136 $redirectUrl = get_permalink($confirmation['customPage']);
137 }
138
139 $returnData = array(
140 'message' => $message[0]['message'],
141 'redirectUrl' => $redirectUrl
142 );
143 }
144
145 $returnData = $this->app->applyFilters(
146 'fluentform_submission_confirmation', $returnData, $form, $confirmation
147 );
148
149 wp_send_json_success(array(
150 'insert_id' => $insertId,
151 'result' => $returnData
152 ), 200);
153 }
154
155 /**
156 * Validate form data.
157 *
158 * @param $form
159 * @param $fields
160 *
161 * @return bool
162 */
163 private function validate($form, &$fields)
164 {
165 $this->validateRestrictions($form, $fields);
166
167 $this->validateNonce($form->id);
168
169 $this->validateReCaptcha();
170
171 $validations = $this->parser->getValidations($this->formData, $fields);
172
173 // Fire an event so that one can hook into it to work with the rules & messages.
174 $validations = $this->app->applyFilters('fluentform_validations', $validations, $form, $this->parser);
175
176 $validator = \FluentValidator\Validator::make($this->formData, $validations[0], $validations[1]);
177
178 if ($validator->validate()->fails()) {
179 $errors = [];
180
181 foreach ($validator->errors() as $attribute => $rules) {
182 $position = strpos($attribute, ']');
183
184 if ($position) {
185 $attribute = substr($attribute, 0, strpos($attribute, ']') + 1);
186 }
187
188 $errors[$attribute] = $rules;
189 }
190
191 // Fire an event so that one can hook into it to work with the errors.
192 $errors = $this->app->applyFilters('fluentform_validation_error', $errors, $form);
193
194 wp_send_json(['errors' => $errors], 423);
195 }
196
197 return true;
198 }
199
200 /**
201 * Validate nonce.
202 *
203 * @param $formId
204 */
205 protected function validateNonce($formId)
206 {
207 $shouldVerifyNonce = $this->app->applyFilters('fluentform_nonce_verify', true, $formId);
208
209 if ($shouldVerifyNonce) {
210 $nonce = ArrayHelper::get($this->formData, '_fluentform_'.$formId.'_fluentformnonce');
211 if (! wp_verify_nonce($nonce, 'fluentform-submit-form')) {
212 $errors = $this->app->applyFilters('fluentForm_nonce_error', [
213 '_fluentformnonce' => [
214 __('Nonce verification failed, please try again.', 'fluentform')
215 ]
216 ]);
217
218 wp_send_json(['errors' => $errors], 423);
219 }
220 }
221 }
222
223 /**
224 * Validate reCaptcha.
225 *
226 * @param $formId
227 */
228 private function validateReCaptcha()
229 {
230 if ($this->parser->hasElement('recaptcha')) {
231 $isValid = ReCaptcha::validate(ArrayHelper::get($this->formData, 'g-recaptcha-response'));
232
233 if (! $isValid) {
234 wp_send_json([
235 'errors' => [
236 'g-recaptcha-response' => [
237 __('reCaptcha verification failed, please try again.', 'fluentform')
238 ]
239 ]
240 ], 423);
241 }
242 }
243 }
244
245 /**
246 * Validate form data based on the form restrictions settings.
247 *
248 * @param \stdClass $form
249 * @param $fields
250 */
251 private function validateRestrictions($form, &$fields)
252 {
253 $formSettings = wpFluent()->table('fluentform_form_meta')->where('form_id', $form->id)
254 ->where('meta_key', 'formSettings')
255 ->first();
256
257 $form->settings = $formSettings ? json_decode($formSettings->value, true) : [];
258
259 $isAllowed = [
260 'status' => true,
261 'message' => ''
262 ];
263
264 // This will check the following restriction settings.
265 // 1. limitNumberOfEntries
266 // 2. scheduleForm
267 // 3. requireLogin
268 $isAllowed = apply_filters('fluentform_is_form_renderable', $isAllowed, $form);
269
270 if (! $isAllowed['status']) {
271 wp_send_json([
272 'errors' => [
273 'restricted' => [
274 __($isAllowed['message'], 'fluentform')
275 ]
276 ]
277 ], 423);
278 }
279
280 // Since we are here, we should now handle if the form should be allowed to submit empty.
281 $restrictions = ArrayHelper::get($form->settings, 'restrictions.denyEmptySubmission', []);
282 $this->handleDenyEmptySubmission($restrictions, $fields);
283 }
284
285 /**
286 * Handle response when empty form submission is not allowed.
287 *
288 * @param array $settings
289 * @param $fields
290 */
291 private function handleDenyEmptySubmission($settings = [], &$fields)
292 {
293 // Determine whether empty form submission is allowed or not.
294 if (ArrayHelper::get($settings, 'enabled')) {
295 // confirm this form has no required fields.
296 if (! $this->parser->hasRequiredFields($fields)) {
297 // Filter out the form data which doesn't have values.
298 $filteredFormData = array_filter(
299 // Filter out the other meta fields that aren't actual inputs.
300 array_intersect_key($this->formData, $fields)
301 );
302
303 if (! count($filteredFormData)) {
304 wp_send_json([
305 'errors' => [
306 'restricted' => [
307 __(ArrayHelper::get($settings, 'message'), 'fluentform')
308 ]
309 ]
310 ], 423);
311 }
312 }
313 }
314 }
315
316 /**
317 * Prepare the data to be inserted to the database.
318 *
319 * @param $formId
320 *
321 * @return array
322 */
323 public function prepareInsertData($formId, $formData = false)
324 {
325 if(!$formData) {
326 $formData = $this->formData;
327 }
328 $previousItem = wpFluent()->table('fluentform_submissions')
329 ->where('form_id', $formId)
330 ->orderBy('id', 'DESC')
331 ->first();
332
333 $serialNumber = 1;
334
335 if ($previousItem) {
336 $serialNumber = $previousItem->serial_number + 1;
337 }
338
339 $browser = new Browser;
340
341 $formData = apply_filters('fluentform_insert_response_data', $formData, $formId);
342
343 return [
344 'form_id' => $formId,
345 'serial_number' => $serialNumber,
346 'response' => json_encode($formData),
347 'source_url' => site_url(ArrayHelper::get($formData, '_wp_http_referer')),
348 'user_id' => get_current_user_id(),
349 'browser' => $browser->getBrowser(),
350 'device' => $browser->getPlatform(),
351 'ip' => $this->app->request->getIp(),
352 'created_at' => date("Y-m-d H:i:s"),
353 'updated_at' => date("Y-m-d H:i:s")
354 ];
355 }
356
357 /**
358 * Delegate the validation rules & messages to the
359 * ones that the validation library recognizes.
360 *
361 * @param $rules
362 * @param $messages
363 *
364 * @return array
365 */
366 protected function delegateValidations($rules, $messages, $search = [], $replace = [])
367 {
368 $search = $search ?: ['max_file_size', 'allowed_file_types'];
369 $replace = $replace ?: ['max', 'mimes'];
370
371 foreach ($rules as &$rule) {
372 $rule = str_replace($search, $replace, $rule);
373 }
374
375 foreach ($messages as $key => $message) {
376 $newKey = str_replace($search, $replace, $key);
377 $messages[$newKey] = $message;
378 unset($messages[$key]);
379 }
380
381 return [$rules, $messages];
382 }
383 }
384