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

670 lines 14.8 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.23 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' => $title . ' (' . $id . ')',
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' => $title . ' (' . $id . ')',
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 // Check honeypot
211 if (isset($fields_values['hnpt']) && !empty($fields_values['hnpt']))
212 {
213 return [
214 'error' => true,
215 'message' => firebox()->_('FB_HONEYPOT_FIELD_TRIGGERED')
216 ];
217 }
218
219 // Remove honeypot field
220 unset($fields_values['hnpt']);
221
222 // Delete form field values unrelated to actual 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 $field_value = isset($fields_values[$field_name]) ? $fields_values[$field_name] : '';
242
243 // If it's not required and the value is empty, we don't need to validate
244 if (!$field->isRequired() && empty($field_value))
245 {
246 unset($fields_values[$field_name]);
247 continue;
248 }
249
250 // Validate class
251 if (!$field->validate($field_value))
252 {
253 $validation_message = $field->getValidationMessage();
254 $error_msgs[] = $field->getLabel() . ': ' . $validation_message;
255
256 $validation[] = [
257 'name' => $field_name,
258 'label' => $field->getLabel(),
259 'type' => $field->getOptionValue('type'),
260 'validation_message' => $validation_message
261 ];
262 }
263
264 $fields_values[$field_name] = $field_value;
265 }
266
267 return $validation ? ['error' => $validation, 'message' => implode('<br />', $error_msgs)] : $form_fields;
268 }
269
270 /**
271 * Finds all supported blocks recursively.
272 *
273 * @param array $block
274 * @param array $supported_blocks
275 *
276 * @return array
277 */
278 private static function findRecursiveBlocks($block, $supported_blocks)
279 {
280 $matching_blocks = [];
281
282 if (in_array($block['blockName'], $supported_blocks))
283 {
284 $matching_blocks[] = $block;
285 }
286
287 if (!empty($block['innerBlocks']))
288 {
289 foreach ($block['innerBlocks'] as $innerBlockItem)
290 {
291 $innerBlocks = self::findRecursiveBlocks($innerBlockItem, $supported_blocks);
292
293 if (!empty($innerBlocks))
294 {
295 $matching_blocks = array_merge($matching_blocks, $innerBlocks);
296 }
297 }
298 }
299
300 return $matching_blocks;
301 }
302
303 /**
304 * Return the form fields.
305 *
306 * @param array $form
307 *
308 * @return array
309 */
310 public static function getFormFields($form)
311 {
312 // Find all supported fields
313 $supported_blocks = self::getSupportedBlocks();
314
315 $form_fields = [];
316
317 // Find form blocks
318 foreach ($form['innerBlocks'] as $key => $block)
319 {
320 // Find supported block
321 if (!$found_blocks = self::findRecursiveBlocks($block, $supported_blocks))
322 {
323 continue;
324 }
325
326 foreach ($found_blocks as $_block)
327 {
328 $field_payload = [
329 'id' => $_block['attrs']['uniqueId'],
330 'label' => Field::getFieldLabel($_block),
331 'type' => Field::getFieldType($_block['blockName']),
332 'name' => Field::getFieldName($_block)
333 ];
334 $final_field_payload = array_merge($field_payload, $_block['attrs']);
335
336 /**
337 * This is nuts.
338 *
339 * WordPress doesn't provide us directly with the block default attribute values
340 * if we haven't edited the block yet.
341 *
342 * So we have to manually set the default values for the fields.
343 */
344 if (in_array($final_field_payload['type'], ['dropdown', 'radio', 'checkbox']))
345 {
346 if (!isset($final_field_payload['choices']))
347 {
348 $final_field_payload['choices'] = [
349 [
350 'default' => false,
351 'value' => 1,
352 'label' => 'Choice 1',
353 'image' => ''
354 ],
355 [
356 'default' => false,
357 'value' => 2,
358 'label' => 'Choice 2',
359 'image' => ''
360 ],
361 [
362 'default' => false,
363 'value' => 3,
364 'label' => 'Choice 3',
365 'image' => ''
366 ]
367 ];
368 }
369 }
370 else if ($final_field_payload['type'] === 'rating')
371 {
372 // Add icon
373 if (!isset($final_field_payload['icon']))
374 {
375 $final_field_payload['icon'] = 'star';
376 }
377
378 // Add size
379 if (!isset($final_field_payload['size']))
380 {
381 $final_field_payload['size'] = 24;
382 }
383
384 // Add maxRating
385 if (!isset($final_field_payload['maxRating']))
386 {
387 $final_field_payload['maxRating'] = 5;
388 }
389
390 // Add halfRatings
391 if (!isset($final_field_payload['halfRatings']))
392 {
393 $final_field_payload['halfRatings'] = false;
394 }
395
396 // Add selectedColor
397 if (!isset($final_field_payload['selectedColor']))
398 {
399 $final_field_payload['selectedColor'] = '#f6cc01';
400 }
401
402 // Add unselectedColor
403 if (!isset($final_field_payload['unselectedColor']))
404 {
405 $final_field_payload['unselectedColor'] = '#bdbdbd';
406 }
407 }
408
409 $form_fields[] = Field::getFieldClass($final_field_payload);
410 }
411 }
412
413 return $form_fields;
414 }
415
416 /**
417 * Returns the form given its ID.
418 *
419 * @param string $form_id
420 *
421 * @return array
422 */
423 public static function getFormByID($form_id = null)
424 {
425 $forms = self::getForms();
426
427 foreach ($forms as $form)
428 {
429 if ($form['id'] !== $form_id)
430 {
431 continue;
432 }
433
434 return $form;
435 }
436
437 return false;
438 }
439
440 /**
441 * Returns the supported blocks.
442 *
443 * @param bool $clean Whether to return only the name of the field without the prefix "firebox/"
444 *
445 * @return array
446 */
447 public static function getSupportedBlocks($clean = false)
448 {
449 $blocks = array_diff(scandir(FBOX_PLUGIN_DIR . 'Inc/Core/Form/Fields/Fields'), ['index.php', '.', '..', '.DS_Store']);
450
451 $data = [];
452
453 foreach ($blocks as $key => $name)
454 {
455 // Strip .php
456 $name = rtrim($name, '.php');
457
458 $data[] = (!$clean ? 'firebox/' : '') . strtolower($name);
459 }
460
461 return $data;
462 }
463
464 /**
465 * Store submission.
466 *
467 * @param string $form_id
468 * @param array $form_settings
469 * @param array $valid_fields
470 * @param array $fields_values
471 * @param bool $save
472 *
473 * @return array
474 */
475 public static function storeSubmission($form_id, $form_settings, $valid_fields, $fields_values, $save = true)
476 {
477 $submissionDefaultState = isset($form_settings['attrs']['submissionDefaultState']) ? $form_settings['attrs']['submissionDefaultState'] : '1';
478
479 if (!$submission_data = Submission::create($form_id, $submissionDefaultState, $save))
480 {
481 return false;
482 }
483
484 if (!$submission_meta_data = SubmissionMeta::create($submission_data['id'], $fields_values, $save))
485 {
486 return false;
487 }
488
489 return self::prepare($submission_data, $valid_fields, $fields_values);
490 }
491
492 /**
493 * Prepare fields.
494 *
495 * @param array $submission
496 * @param array $valid_fields
497 * @param array $fields_values
498 *
499 * @return array
500 */
501 private static function prepare($submission, $valid_fields, $fields_values)
502 {
503 $prepared_data = $submission;
504 $prepared_data['prepared_fields'] = [];
505
506 foreach ($valid_fields as $key => $field)
507 {
508 $field_name = $field->getOptionValue('name');
509 $field_id = $field->getOptionValue('id');
510 $field_value = isset($fields_values[$field_id]) ? $fields_values[$field_id] : '';
511
512 $field->setValue($field_value);
513
514 $prepared_data['prepared_fields'][$field_name] = [
515 'class' => $field,
516 'submitted_value' => $field_value,
517 'value' => $field->prepareValue($field_value),
518 'value_html' => $field->prepareValueHTML($field_value),
519 'value_raw' => $field->prepareRawValue($field_value)
520 ];
521 }
522
523 return $prepared_data;
524 }
525
526 /**
527 * Ensure the popup has unique Form IDs.
528 *
529 * @param string $content
530 *
531 * @return void
532 */
533 public static function ensureUniqueFormIDs(&$content)
534 {
535 // Get forms
536 $forms = self::getForms();
537
538 // Get form IDs in content
539 $pattern = '/wp:firebox\/form {"uniqueId":"(.*?)"/';
540
541 // Find matches
542 preg_match_all($pattern, $content, $matches);
543
544 // Ensure we have at least one form in the popup
545 if (!isset($matches[1]) || empty($matches[1]))
546 {
547 return;
548 }
549
550 $old_form_ids_in_popup = $matches[1];
551 $new_form_ids_in_popup = [];
552
553 // Find new IDs
554 foreach ($old_form_ids_in_popup as $key => $id)
555 {
556 while (true)
557 {
558 $form = array_filter($forms, function($form_item) use ($id) {
559 return $id === $form_item['id'];
560 });
561 $form_id = reset($form);
562
563 // Form ID is unique
564 if (!$form_id)
565 {
566 $new_form_ids_in_popup[] = $id;
567 break;
568 }
569
570 // Form ID is not unique, generate new
571 $id = md5(uniqid());
572 $id = substr($id, 0, 12);
573
574 // Add dash after 6th character
575 $id = substr_replace($id, '-', 6, 0);
576
577 // Add dash after 10th character
578 $id = substr_replace($id, '-', 11, 0);
579 }
580 }
581
582 if (count($old_form_ids_in_popup) !== count($new_form_ids_in_popup))
583 {
584 return;
585 }
586
587 // Replace old IDs with new IDs
588 foreach ($old_form_ids_in_popup as $index => $id)
589 {
590 // Replace unique ID
591 $content = str_replace('"uniqueId":"' . $id . '"', '"uniqueId":"' . $new_form_ids_in_popup[$index] . '"', $content);
592
593 // Replace all other instances
594 $content = str_replace('form-' . $id, 'form-' . $new_form_ids_in_popup[$index], $content);
595 }
596 }
597
598 /**
599 * Replaces Smart Tags.
600 *
601 * @param array $attrs
602 * @param array $fields_values
603 * @param array $submission
604 *
605 * @return void
606 */
607 public static function replaceSmartTags(&$attrs, $fields_values, $submission)
608 {
609 // Replace Smart Tags
610 $tags = new \FPFramework\Base\SmartTags\SmartTags();
611
612 // register FB Smart Tags
613 $tags->register('\FireBox\Core\Form\SmartTags', FBOX_BASE_FOLDER . '/Inc/Core/Form/SmartTags', [
614 'field_values' => $fields_values,
615 'submission' => $submission
616 ]);
617
618 $attrs = $tags->replace($attrs);
619 }
620
621 /**
622 * Returns the submission action.
623 *
624 * @param array $attrs
625 *
626 * @return array
627 */
628 public static function getSubmissionAction($attrs)
629 {
630 $action = isset($attrs['submissionAction']) ? $attrs['submissionAction'] : 'message';
631
632 $payload = [
633 'action' => $action,
634 'message' => $action === 'message' ? (isset($attrs['messageAfterSuccess']) ? $attrs['messageAfterSuccess'] : 'Thanks for contacting us! We will get in touch with you shortly.') : '',
635 'resetForm' => isset($attrs['resetForm']) ? $attrs['resetForm'] : true,
636 'hideForm' => isset($attrs['hideForm']) ? $attrs['hideForm'] : true
637 ];
638
639 if ($action === 'redirect')
640 {
641 $payload['redirectURL'] = isset($attrs['redirectURL']) ? $attrs['redirectURL'] : '';
642 }
643
644 return $payload;
645 }
646
647 public static function getSubmissions($form_id = null)
648 {
649 if (!$form_id)
650 {
651 return;
652 }
653
654 $data = [
655 'where' => [
656 'form_id' => " = '" . esc_sql($form_id) . "'"
657 ],
658 'limit' => 1000
659 ];
660
661 $submissions = firebox()->tables->submission->getResults($data, true);
662
663 foreach ($submissions as &$submission)
664 {
665 $submission->meta = SubmissionMeta::getMeta($submission->id);
666 }
667
668 return $submissions;
669 }
670 }