PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 2.1.14
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v2.1.14
3.1.13 3.1.12 3.1.11 3.1.10 3.1.9 3.1.8 3.1.7 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 All 122 releases
firebox / Inc / Core / Helpers / Form / Form.php

Form.php in FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment 2.1.14, at Inc/Core/Helpers/Form/Form.php

633 lines 14.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package FireBox
4 * @version 2.1.14 Free
5 *
6 * @author FirePlugins <info@fireplugins.com>
7 * @link https://www.fireplugins.com
8 * @copyright Copyright © 2024 FirePlugins All Rights Reserved
9 * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
10 */
11
12 namespace FireBox\Core\Helpers\Form;
13
14 if (!defined('ABSPATH'))
15 {
16 exit; // Exit if accessed directly.
17 }
18
19 use \FireBox\Core\Helpers\BoxHelper;
20 use \FPFramework\Helpers\Plugins\FireBox\Form as FrameworkFireBoxForm;
21
22 class Form
23 {
24 /**
25 * Checks whether a form is valid given its ID.
26 *
27 * @param string $form_id
28 *
29 * @return string
30 */
31 public static function isValid($form_id = null)
32 {
33 if (!$form_id)
34 {
35 return false;
36 }
37
38 // Validate Form ID
39 // Get all popups
40 $popups = BoxHelper::getAllBoxes(['publish', 'draft']);
41 $popups = BoxHelper::produceKeyValueBoxes($popups->posts);
42
43 // Whether the Form ID is valid
44 $valid_form_id = false;
45
46 if (!$forms = self::getForms())
47 {
48 return false;
49 }
50
51 foreach ($forms as $key => $form)
52 {
53 if ('form-' . $form['id'] !== $form_id)
54 {
55 continue;
56 }
57
58 return $form;
59 }
60
61 return;
62 }
63
64 /**
65 * Returns a list of forms with form id => form title key,value pair.
66 *
67 * @return array
68 */
69 public static function getParsedForms()
70 {
71 // cache key
72 $hash = md5('FireBox\Core\Belpers\Form::getParsedForms');
73
74 // check cache
75 if ($forms = wp_cache_get($hash))
76 {
77 return;
78 }
79
80 $forms = self::getForms();
81
82 $parsed = [];
83
84 foreach ($forms as $key => $value)
85 {
86 $parsed[$value['id']] = $value['name'];
87 }
88
89 // set cache
90 wp_cache_set($hash, $parsed, $hash);
91
92 return $parsed;
93 }
94
95 public static function getCampaignForms($campaigns)
96 {
97 $forms = [];
98
99 // Find forms
100 foreach ($campaigns as $id => $title)
101 {
102 if (!has_block('firebox/form', $id))
103 {
104 continue;
105 }
106
107 $campaign_modified_gmt = get_post_modified_time('U', true, $id);
108 $campaign_gmt = get_post_time('U', true, $id);
109 $campaign_status = get_post_status($id);
110
111 $blocks = parse_blocks(get_the_content(null, false, $id));
112
113 foreach ($blocks as $key => $block)
114 {
115 if (isset($block['innerBlocks']))
116 {
117 foreach ($block['innerBlocks'] as $innerBlock)
118 {
119 // Find form block
120 if (!$form_block = FrameworkFireBoxForm::findRecursiveForm($innerBlock))
121 {
122 continue;
123 }
124
125 $atts = isset($form_block['attrs']) ? $form_block['attrs'] : false;
126 if (!$atts)
127 {
128 continue;
129 }
130
131 $block_unique_id = isset($atts['uniqueId']) ? $atts['uniqueId'] : false;
132 if (!$block_unique_id)
133 {
134 continue;
135 }
136
137 $forms[] = [
138 'id' => $block_unique_id,
139 'block' => $form_block,
140 'created_at' => $campaign_modified_gmt ? $campaign_modified_gmt : $campaign_gmt,
141 'state' => $campaign_status === 'publish' ? '1' : '0',
142 'name' => isset($form_block['attrs']['formName']) ? $form_block['attrs']['formName'] : firebox()->_('FB_UNTITLED_FORM'),
143 'fields' => self::getFormFields($form_block)
144 ];
145 }
146 }
147
148 if ($block['blockName'] !== 'firebox/form')
149 {
150 continue;
151 }
152
153 $atts = isset($block['attrs']) ? $block['attrs'] : false;
154 if (!$atts)
155 {
156 continue;
157 }
158
159 $block_unique_id = isset($atts['uniqueId']) ? $atts['uniqueId'] : false;
160 if (!$block_unique_id)
161 {
162 continue;
163 }
164
165 $forms[] = [
166 'id' => $block_unique_id,
167 'block' => $block,
168 'created_at' => $campaign_modified_gmt ? $campaign_modified_gmt : $campaign_gmt,
169 'state' => $campaign_status === 'publish' ? '1' : '0',
170 'name' => isset($block['attrs']['formName']) ? $block['attrs']['formName'] : firebox()->_('FB_UNTITLED_FORM'),
171 'fields' => self::getFormFields($block)
172 ];
173 }
174 }
175
176 return $forms;
177 }
178
179 /**
180 * Gets all forms.
181 *
182 * @return array
183 */
184 public static function getForms()
185 {
186 // Get all popups
187 $popups_data = BoxHelper::getAllBoxes(['publish', 'draft']);
188 $campaigns = BoxHelper::produceKeyValueBoxes($popups_data->posts);
189
190 return self::getCampaignForms($campaigns);
191 }
192
193 /**
194 * Validates the form.
195 *
196 * @param array $form_fields
197 * @param array $fields_values
198 *
199 * @return array
200 */
201 public static function validate($form_fields = [], &$fields_values = [])
202 {
203 if (!$form_fields || !$fields_values)
204 {
205 return false;
206 }
207
208 $validation = [];
209
210 // Remove honeypot field
211 unset($fields_values['hnpt']);
212
213 // Check honeypot
214 if (isset($fields_values['hnpt']) && !empty($fields_values['hnpt']))
215 {
216 return [
217 'error' => true,
218 'message' => firebox()->_('FB_HONEYPOT_FIELD_TRIGGERED')
219 ];
220 }
221
222 // Ensure the fields values are based on valid form fields
223 foreach ($fields_values as $field_name => $field_value)
224 {
225 $valid_value = count(array_filter($form_fields, function($field) use ($field_name) {
226 return $field_name === $field->getOptionValue('name');
227 }));
228
229 if (!$valid_value)
230 {
231 unset($fields_values[$field_name]);
232 }
233 }
234
235 $error_msgs = [];
236
237 // Validate fields
238 foreach ($form_fields as $field)
239 {
240 $field_name = $field->getOptionValue('name');
241
242 // Whether this is an array that contains a "value" key that stores the value or if its the whole value of the key $field_name
243 $field_value = isset($fields_values[$field_name]) && isset($fields_values[$field_name]['value']) ? $fields_values[$field_name]['value'] : (isset($fields_values[$field_name]) ? $fields_values[$field_name] : '');
244
245 // Validate class
246 if (!$field->validate($field_value))
247 {
248 $validation_message = $field->getValidationMessage();
249 $error_msgs[] = $field->getLabel() . ': ' . $validation_message;
250
251 $validation[] = [
252 'name' => $field_name,
253 'label' => $field->getLabel(),
254 'type' => $field->getOptionValue('type'),
255 'validation_message' => $validation_message
256 ];
257 }
258
259 // Update field value after validation
260 if (isset($fields_values[$field_name]['value']))
261 {
262 $fields_values[$field_name]['value'] = $field_value;
263 }
264 else
265 {
266 $fields_values[$field_name] = $field_value;
267 }
268 }
269
270 return $validation ? ['error' => $validation, 'message' => implode('<br />', $error_msgs)] : $form_fields;
271 }
272
273 /**
274 * Finds all supported blocks recursively.
275 *
276 * @param array $block
277 * @param array $supported_blocks
278 *
279 * @return array
280 */
281 private static function findRecursiveBlocks($block, $supported_blocks)
282 {
283 $matching_blocks = [];
284
285 if (in_array($block['blockName'], $supported_blocks))
286 {
287 $matching_blocks[] = $block;
288 }
289
290 if (!empty($block['innerBlocks']))
291 {
292 foreach ($block['innerBlocks'] as $innerBlockItem)
293 {
294 $innerBlocks = self::findRecursiveBlocks($innerBlockItem, $supported_blocks);
295
296 if (!empty($innerBlocks))
297 {
298 $matching_blocks = array_merge($matching_blocks, $innerBlocks);
299 }
300 }
301 }
302
303 return $matching_blocks;
304 }
305
306 /**
307 * Return the form fields.
308 *
309 * @param array $form
310 *
311 * @return array
312 */
313 public static function getFormFields($form)
314 {
315 // Find all supported fields
316 $supported_blocks = self::getSupportedBlocks();
317
318 $form_fields = [];
319
320 // Find form blocks
321 foreach ($form['innerBlocks'] as $key => $block)
322 {
323 // Find supported block
324 if (!$found_blocks = self::findRecursiveBlocks($block, $supported_blocks))
325 {
326 continue;
327 }
328
329 foreach ($found_blocks as $_block)
330 {
331 $field_payload = [
332 'id' => $_block['attrs']['uniqueId'],
333 'label' => Field::getFieldLabel($_block),
334 'type' => Field::getFieldType($_block['blockName']),
335 'name' => Field::getFieldName($_block)
336 ];
337 $final_field_payload = array_merge($field_payload, $_block['attrs']);
338
339 /**
340 * This is nuts.
341 *
342 * WordPress doesn't provide us directly with the block default attribute values
343 * if we haven't edited the block yet.
344 *
345 * So we have to manually set the default values for the fields.
346 */
347 if (in_array($final_field_payload['type'], ['dropdown', 'radio', 'checkbox']))
348 {
349 if (!isset($final_field_payload['choices']))
350 {
351 $final_field_payload['choices'] = [
352 [
353 'default' => false,
354 'value' => 1,
355 'label' => 'Choice 1',
356 'image' => ''
357 ],
358 [
359 'default' => false,
360 'value' => 2,
361 'label' => 'Choice 2',
362 'image' => ''
363 ],
364 [
365 'default' => false,
366 'value' => 3,
367 'label' => 'Choice 3',
368 'image' => ''
369 ]
370 ];
371 }
372 }
373
374 $form_fields[] = Field::getFieldClass($final_field_payload);
375 }
376 }
377
378 return $form_fields;
379 }
380
381 /**
382 * Returns the form given its ID.
383 *
384 * @param string $form_id
385 *
386 * @return array
387 */
388 public static function getFormByID($form_id = null)
389 {
390 $forms = self::getForms();
391
392 foreach ($forms as $form)
393 {
394 if ($form['id'] !== $form_id)
395 {
396 continue;
397 }
398
399 return $form;
400 }
401
402 return false;
403 }
404
405 /**
406 * Returns the supported blocks.
407 *
408 * @param bool $clean Whether to return only the name of the field without the prefix "firebox/"
409 *
410 * @return array
411 */
412 public static function getSupportedBlocks($clean = false)
413 {
414 $blocks = array_diff(scandir(FBOX_PLUGIN_DIR . 'Inc/Core/Form/Fields/Fields'), ['index.php', '.', '..', '.DS_Store']);
415
416 $data = [];
417
418 foreach ($blocks as $key => $name)
419 {
420 // Strip .php
421 $name = rtrim($name, '.php');
422
423 $data[] = (!$clean ? 'firebox/' : '') . strtolower($name);
424 }
425
426 return $data;
427 }
428
429 /**
430 * Store submission.
431 *
432 * @param string $form_id
433 * @param array $form_settings
434 * @param array $valid_fields
435 * @param array $fields_values
436 * @param bool $save
437 *
438 * @return array
439 */
440 public static function storeSubmission($form_id, $form_settings, $valid_fields, $fields_values, $save = true)
441 {
442 $submissionDefaultState = isset($form_settings['attrs']['submissionDefaultState']) ? $form_settings['attrs']['submissionDefaultState'] : 1;
443
444 if (!$submission_data = Submission::create($form_id, $submissionDefaultState, $save))
445 {
446 return false;
447 }
448
449 if (!$submission_meta_data = SubmissionMeta::create($submission_data['id'], $fields_values, $save))
450 {
451 return false;
452 }
453
454 return self::prepare($submission_data, $valid_fields, $fields_values);
455 }
456
457 /**
458 * Prepare fields.
459 *
460 * @param array $submission
461 * @param array $valid_fields
462 * @param array $fields_values
463 *
464 * @return array
465 */
466 private static function prepare($submission, $valid_fields, $fields_values)
467 {
468 $prepared_data = $submission;
469 $prepared_data['prepared_fields'] = [];
470
471 foreach ($valid_fields as $key => $field)
472 {
473 $field_name = $field->getOptionValue('name');
474 $submitted_value = $fields_values[$field_name];
475
476 $field->setValue($submitted_value['value']);
477
478 $prepared_data['prepared_fields'][$field_name] = [
479 'class' => $field,
480 'value' => $field->prepareValue($submitted_value['value']),
481 'value_html' => $field->prepareValueHTML($submitted_value['value']),
482 'value_raw' => $submitted_value['value']
483 ];
484 }
485
486 return $prepared_data;
487 }
488
489 /**
490 * Ensure the popup has unique Form IDs.
491 *
492 * @param string $content
493 *
494 * @return void
495 */
496 public static function ensureUniqueFormIDs(&$content)
497 {
498 // Get forms
499 $forms = self::getForms();
500
501 // Get form IDs in content
502 $pattern = '/wp:firebox\/form {"uniqueId":"(.*?)"/';
503
504 // Find matches
505 preg_match_all($pattern, $content, $matches);
506
507 // Ensure we have at least one form in the popup
508 if (!isset($matches[1]) || empty($matches[1]))
509 {
510 return;
511 }
512
513 $old_form_ids_in_popup = $matches[1];
514 $new_form_ids_in_popup = [];
515
516 // Find new IDs
517 foreach ($old_form_ids_in_popup as $key => $id)
518 {
519 while (true)
520 {
521 $form = array_filter($forms, function($form_item) use ($id) {
522 return $id === $form_item['id'];
523 });
524 $form_id = reset($form);
525
526 // Form ID is unique
527 if (!$form_id)
528 {
529 $new_form_ids_in_popup[] = $id;
530 break;
531 }
532
533 // Form ID is not unique, generate new
534 $id = md5(uniqid());
535 $id = substr($id, 0, 12);
536
537 // Add dash after 6th character
538 $id = substr_replace($id, '-', 6, 0);
539
540 // Add dash after 10th character
541 $id = substr_replace($id, '-', 11, 0);
542 }
543 }
544
545 if (count($old_form_ids_in_popup) !== count($new_form_ids_in_popup))
546 {
547 return;
548 }
549
550 // Replace old IDs with new IDs
551 foreach ($old_form_ids_in_popup as $index => $id)
552 {
553 // Replace unique ID
554 $content = str_replace('"uniqueId":"' . $id . '"', '"uniqueId":"' . $new_form_ids_in_popup[$index] . '"', $content);
555
556 // Replace all other instances
557 $content = str_replace('form-' . $id, 'form-' . $new_form_ids_in_popup[$index], $content);
558 }
559 }
560
561 /**
562 * Replaces Smart Tags.
563 *
564 * @param array $attrs
565 * @param array $fields_values
566 * @param array $submission
567 *
568 * @return void
569 */
570 public static function replaceSmartTags(&$attrs, $fields_values, $submission)
571 {
572 // Replace Smart Tags
573 $tags = new \FPFramework\Base\SmartTags\SmartTags();
574
575 // register FB Smart Tags
576 $tags->register('\FireBox\Core\Form\SmartTags', FBOX_BASE_FOLDER . '/Inc/Core/Form/SmartTags', [
577 'field_values' => $fields_values,
578 'submission' => $submission
579 ]);
580
581 $attrs = $tags->replace($attrs);
582 }
583
584 /**
585 * Returns the submission action.
586 *
587 * @param array $attrs
588 *
589 * @return array
590 */
591 public static function getSubmissionAction($attrs)
592 {
593 $action = isset($attrs['submissionAction']) ? $attrs['submissionAction'] : 'message';
594
595 $payload = [
596 'action' => $action,
597 'message' => $action === 'message' ? (isset($attrs['messageAfterSuccess']) ? $attrs['messageAfterSuccess'] : 'Thanks for contacting us! We will get in touch with you shortly.') : '',
598 'resetForm' => isset($attrs['resetForm']) ? $attrs['resetForm'] : true,
599 'hideForm' => isset($attrs['hideForm']) ? $attrs['hideForm'] : true
600 ];
601
602 if ($action === 'redirect')
603 {
604 $payload['redirectURL'] = isset($attrs['redirectURL']) ? $attrs['redirectURL'] : '';
605 }
606
607 return $payload;
608 }
609
610 public static function getSubmissions($form_id = null)
611 {
612 if (!$form_id)
613 {
614 return;
615 }
616
617 $data = [
618 'where' => [
619 'form_id' => " = '" . esc_sql($form_id) . "'"
620 ],
621 'limit' => 1000
622 ];
623
624 $submissions = firebox()->tables->submission->getResults($data, true);
625
626 foreach ($submissions as &$submission)
627 {
628 $submission->meta = SubmissionMeta::getMeta($submission->id);
629 }
630
631 return $submissions;
632 }
633 }