PluginProbe
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment / 3.1.5
FireBox – WooCommerce Popup Builder, Exit Intent Popup, Email Optin & Cart Abandonment v3.1.5
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 3.1.5, at Inc/Core/Helpers/Form/Form.php

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