--- a/nelio-ab-testing/admin/views/nelio-ab-testing-settings-page.php
+++ b/nelio-ab-testing/admin/views/nelio-ab-testing-settings-page.php
@@ -17,7 +17,7 @@
<?php echo esc_html_x( 'Nelio A/B Testing - Settings', 'text', 'nelio-ab-testing' ); ?>
</h2>
- <?php settings_errors(); ?>
+ <?php settings_errors( 'nelio-ab-testing' ); ?>
<form method="post" action="options.php" class="nab-settings-form">
<?php
--- a/nelio-ab-testing/assets/dist/js/account-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/account-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '9d6fc8d443d48df6a5b0');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '2b6ba45a549fbd6d9269');
--- a/nelio-ab-testing/assets/dist/js/components.asset.php
+++ b/nelio-ab-testing/assets/dist/js/components.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-data', 'nab-date', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'e685814765abda948704');
+<?php return array('dependencies' => array('lodash', 'nab-data', 'nab-date', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'cec46a266ebead5bac32');
--- a/nelio-ab-testing/assets/dist/js/conversion-action-library.asset.php
+++ b/nelio-ab-testing/assets/dist/js/conversion-action-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-warning'), 'version' => '644c4f90503b2ee82c6a');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-primitives', 'wp-warning'), 'version' => '327ec3f2a78e90eeb2bd');
--- a/nelio-ab-testing/assets/dist/js/css-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/css-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '86cb76406a59a7819f85');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-blob', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6e247e6c3b8d8ee1f5ca');
--- a/nelio-ab-testing/assets/dist/js/data.asset.php
+++ b/nelio-ab-testing/assets/dist/js/data.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-date', 'nab-utils', 'wp-api-fetch', 'wp-data', 'wp-date', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => '7ca645a1fbde882d994a');
+<?php return array('dependencies' => array('lodash', 'nab-date', 'nab-utils', 'wp-api-fetch', 'wp-data', 'wp-date', 'wp-html-entities', 'wp-i18n', 'wp-url'), 'version' => '42c1164ad60f8899a0c2');
--- a/nelio-ab-testing/assets/dist/js/editor.asset.php
+++ b/nelio-ab-testing/assets/dist/js/editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6abfdb2173a4c1962b78');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-block-editor', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '1f423146ffb4b993d60c');
--- a/nelio-ab-testing/assets/dist/js/experiment-library.asset.php
+++ b/nelio-ab-testing/assets/dist/js/experiment-library.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-action-library', 'nab-conversion-actions', 'nab-data', 'nab-experiments', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => '256f253685f8d1d6cba1');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-conversion-action-library', 'nab-conversion-actions', 'nab-data', 'nab-experiments', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-html-entities', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => '21770695d283f680f4a3');
--- a/nelio-ab-testing/assets/dist/js/experiment-list-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/experiment-list-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'fc8e72340b9c6033fe5e');
+<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'b667397b32aff41e6962');
--- a/nelio-ab-testing/assets/dist/js/heatmap-editor.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-editor.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-editor', 'nab-experiment-library', 'nab-experiments', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '5ccbca7f039eebd08cde');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-date', 'nab-editor', 'nab-experiment-library', 'nab-experiments', 'nab-segmentation-rules', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '25fc9acdb716a4fc4258');
--- a/nelio-ab-testing/assets/dist/js/heatmap-results-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/heatmap-results-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-i18n', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '19e9051a68f56c650178');
+<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-i18n', 'nab-segmentation-rule-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '04eb962ed023f92aa0be');
--- a/nelio-ab-testing/assets/dist/js/individual-settings.asset.php
+++ b/nelio-ab-testing/assets/dist/js/individual-settings.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'cd12e032af6f93d120c0');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '6dd20670190a9d199954');
--- a/nelio-ab-testing/assets/dist/js/javascript-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/javascript-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '776cfa8ea9ba010e1bbf');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '7af2b334732362c520e7');
--- a/nelio-ab-testing/assets/dist/js/overview-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/overview-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '73e8d09ba8987f21de1f');
+<?php return array('dependencies' => array('lodash', 'nab-commands', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '9f4dadbfe25b239d106e');
--- a/nelio-ab-testing/assets/dist/js/php-experiment-admin.asset.php
+++ b/nelio-ab-testing/assets/dist/js/php-experiment-admin.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '0bc1a8b4cbe20350939a');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-editor', 'nab-experiment-library', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '229cea6c4f304ba3083b');
--- a/nelio-ab-testing/assets/dist/js/post-experiment-management.asset.php
+++ b/nelio-ab-testing/assets/dist/js/post-experiment-management.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '83b512ac94836e5a9973');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-experiment-library', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-edit-post', 'wp-element', 'wp-i18n', 'wp-plugins', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '5b4b5a4be5f446bfb2c6');
--- a/nelio-ab-testing/assets/dist/js/product-experiment-management.asset.php
+++ b/nelio-ab-testing/assets/dist/js/product-experiment-management.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('jquery', 'lodash', 'nab-components', 'nab-data', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => 'f3c1484667d0b127fcd8');
+<?php return array('dependencies' => array('jquery', 'lodash', 'nab-components', 'nab-data', 'nab-experiments', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-media-utils', 'wp-primitives', 'wp-warning'), 'version' => 'd96e5d8b9d287fa80dc4');
--- a/nelio-ab-testing/assets/dist/js/recordings-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/recordings-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'efb492c1fdae7fb93c09');
+<?php return array('dependencies' => array('lodash', 'nab-components', 'nab-data', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-api-fetch', 'wp-components', 'wp-compose', 'wp-data', 'wp-date', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '39f281b135cefd462078');
--- a/nelio-ab-testing/assets/dist/js/results-page.asset.php
+++ b/nelio-ab-testing/assets/dist/js/results-page.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array('lodash', 'moment', 'nab-commands', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => 'b003b73855824b1d4974');
+<?php return array('dependencies' => array('lodash', 'moment', 'nab-commands', 'nab-components', 'nab-conversion-actions', 'nab-data', 'nab-date', 'nab-experiment-library', 'nab-experiments', 'nab-i18n', 'nab-utils', 'react', 'react-dom', 'react-jsx-runtime', 'wp-components', 'wp-compose', 'wp-data', 'wp-dom-ready', 'wp-element', 'wp-i18n', 'wp-notices', 'wp-primitives', 'wp-url', 'wp-warning'), 'version' => '5837b1be9f8828cfbc7c');
--- a/nelio-ab-testing/includes/hooks/compat/contact-form-7/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/contact-form-7/index.php
@@ -1,78 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatContactForm7;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
- $data['wpcf7_contact_form'] = array(
- 'name' => 'wpcf7_contact_form',
- 'label' => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
- 'labels' => array(
- 'singular_name' => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
- ),
- 'kind' => 'form',
- );
- return $data;
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action Action.
- * @param int $experiment_id Experiment ID.
- * @param int $goal_index Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
- if ( 'wpcf7_contact_form' !== $action['formType'] ) {
- return;
- }
- add_action(
- 'wpcf7_submit',
- function ( $form, $result ) use ( $action, $experiment_id, $goal_index ) {
- /** @var WPCF7_ContactForm $form */
- /** @var array{status:string} $result */
-
- if ( $action['formId'] !== $form->id() ) {
- return;
- }
- if ( ! in_array( $result['status'], array( 'mail_sent', 'demo_mode' ), true ) ) {
- return;
- }
- maybe_sync_event_submission( $experiment_id, $goal_index );
- },
- 10,
- 2
- );
-}
-
-add_action(
- 'plugins_loaded',
- function () {
- if ( ! function_exists( 'is_plugin_active' ) ) {
- nab_require_wp_file( '/wp-admin/includes/plugin.php' );
- }
-
- if ( ! is_plugin_active( 'contact-form-7/wp-contact-form-7.php' ) ) {
- return;
- }
-
- add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
- add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
- }
-);
--- a/nelio-ab-testing/includes/hooks/compat/fluent-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/fluent-forms/index.php
@@ -1,233 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatFluentForms;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
- $data['nab_fluent_form'] = array(
- 'name' => 'nab_fluent_form',
- 'label' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
- 'labels' => array(
- 'singular_name' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
- ),
- 'kind' => 'form',
- );
- return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post The post to filter.
- * @param int|string $post_id The id of the post.
- * @param string $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_fluent_form( $post, $post_id, $post_type ) {
- if ( null !== $post ) {
- return $post;
- }
-
- if ( 'nab_fluent_form' !== $post_type ) {
- return $post;
- }
-
- $form = FluentFormAppModelsForm::where( 'id', $post_id )->first();
-
- if ( empty( $form ) ) {
- return new WP_Error(
- 'not-found',
- sprintf(
- /* translators: %d: Form ID. */
- _x( 'Fluent form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
- $post_id
- )
- );
- }
-
- return array(
- 'id' => absint( $form['id'] ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form['title'],
- 'type' => 'nab_fluent_form',
- 'typeLabel' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result The result data.
- * @param string $post_type The post type.
- * @param string $query The query term.
- * @param int $per_page The number of posts to show per page.
- * @param int $page The number of the current page.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_fluent_forms( $result, $post_type, $query, $per_page, $page ) {
- if ( null !== $result ) {
- return $result;
- }
-
- if ( 'nab_fluent_form' !== $post_type ) {
- return $result;
- }
-
- $forms = FluentFormAppModelsForm::select( array( 'id', 'title', 'status' ) )
- ->where( 'status', 'published' )
- ->orderBy( 'title', 'DESC' )
- ->get();
- $forms = $forms->getDictionary();
- $forms = array_map( fn( $f ) => $f->getAttributes(), $forms );
- /** @var list<array{id:numeric, title:string, status:string}> */
- $forms = array_values( $forms );
-
- $query = strtolower( trim( $query ) );
- if ( ! empty( $query ) ) {
- $form_id = absint( $query );
- $forms = array_values(
- array_filter(
- $forms,
- fn( $form ) => (
- absint( $form['id'] ) === absint( $form_id ) ||
- false !== strpos( strtolower( $form['title'] ), $query )
- )
- )
- );
- }
-
- $forms = array_map(
- function ( $form ) {
- return array(
- 'id' => absint( $form['id'] ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form['title'],
- 'type' => 'nab_fluent_form',
- 'typeLabel' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
- },
- $forms
- );
-
- return array(
- 'results' => $forms,
- 'pagination' => array(
- 'more' => count( $forms ) === $per_page,
- 'pages' => empty( $page ) ? 1 : $page,
- ),
- );
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action Action.
- * @param int $experiment_id Experiment ID.
- * @param int $goal_index Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
- if ( 'nab_fluent_form' !== $action['formType'] ) {
- return;
- }
- add_action(
- 'fluentform/notify_on_form_submit',
- function ( $entry_id, $form_data, $form ) use ( $action, $experiment_id, $goal_index ) {
- /** @var TIgnore $entry_id */
- /** @var TIgnore $form_data */
- /** @var FluentFormAppModelsForm $form */
-
- if ( absint( $form->getAttributes()['id'] ) !== absint( $action['formId'] ) ) {
- return;
- }
- $args = array();
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- if ( isset( $_REQUEST['data'] ) && is_string( $_REQUEST['data'] ) ) {
- try {
- $args = array_reduce(
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- explode( '&', sanitize_text_field( wp_unslash( $_REQUEST['data'] ) ) ),
- function ( $r, $i ) {
- /** @var array<string,string> $r */
- /** @var string $i */
-
- $arg = explode( '=', $i );
- $r[ $arg[0] ] = urldecode( $arg[1] );
- return $r;
- },
- array()
- );
- } catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
- }
- }
- foreach ( $args as $name => $value ) {
- if ( 0 !== strpos( $name, 'nab_' ) ) {
- continue;
- }
- // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- if ( ! isset( $_REQUEST[ $name ] ) ) {
- $_POST[ $name ] = $value;
- $_REQUEST[ $name ] = $value;
- }
- }
- maybe_sync_event_submission( $experiment_id, $goal_index );
- },
- 10,
- 3
- );
-}
-
-add_action(
- 'plugins_loaded',
- function () {
- if ( ! function_exists( 'is_plugin_active' ) ) {
- nab_require_wp_file( '/wp-admin/includes/plugin.php' );
- }
-
- if ( ! is_plugin_active( 'fluentform/fluentform.php' ) && ! is_plugin_active( 'fluentform-pro/fluentform-pro.php' ) ) {
- return;
- }
-
- add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
- add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_fluent_form', 10, 3 );
- add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_fluent_forms', 10, 5 );
- add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
- }
-);
--- a/nelio-ab-testing/includes/hooks/compat/formcraft/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/formcraft/index.php
@@ -1,207 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatFormcraft;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
- $data['nab_formcraft_form'] = array(
- 'name' => 'nab_formcraft_form',
- 'label' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
- 'labels' => array(
- 'singular_name' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
- ),
- 'kind' => 'form',
- );
- return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post The post to filter.
- * @param int|string $post_id The id of the post.
- * @param string $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_formcraft_form( $post, $post_id, $post_type ) {
- if ( null !== $post ) {
- return $post;
- }
-
- if ( 'nab_formcraft_form' !== $post_type ) {
- return $post;
- }
-
- /** @var wpdb $wpdb */
- global $wpdb;
- $forms_table = $wpdb->prefix . 'formcraft_b_forms';
- // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
- $form = $wpdb->get_row(
- $wpdb->prepare(
- 'SELECT * FROM %i WHERE id=%d',
- $forms_table,
- $post_id
- ),
- ARRAY_A
- );
-
- if ( empty( $form ) ) {
- return new WP_Error(
- 'not-found',
- sprintf(
- /* translators: %d: Form ID. */
- _x( 'FormCraft form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
- $post_id
- )
- );
- }
-
- return array(
- 'id' => absint( $post_id ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => is_string( $form['name'] ) ? $form['name'] : "$post_id",
- 'type' => 'nab_formcraft_form',
- 'typeLabel' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result The result data.
- * @param string $post_type The post type.
- * @param string $query The query term.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_formcraft_forms( $result, $post_type, $query ) {
- if ( null !== $result ) {
- return $result;
- }
-
- if ( 'nab_formcraft_form' !== $post_type ) {
- return $result;
- }
-
- /** @var wpdb $wpdb */
- global $wpdb;
- $forms_table = $wpdb->prefix . 'formcraft_b_forms';
- /** @var list<object{id:int,name:string}> */
-
- // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
- $forms = $wpdb->get_results(
- $wpdb->prepare(
- 'SELECT * FROM %i',
- $forms_table
- )
- );
-
- if ( ! empty( $query ) ) {
- $forms = array_values(
- array_filter(
- $forms,
- fn( $f ) => absint( $query ) === absint( $f->id ) || false !== strpos( strtolower( $f->name ), strtolower( $query ) ),
- )
- );
- }
-
- $forms = array_map(
- function ( $form ) {
- return array(
- 'id' => absint( $form->id ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form->name,
- 'type' => 'nab_formcraft_form',
- 'typeLabel' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
- },
- $forms
- );
-
- return array(
- 'results' => $forms,
- 'pagination' => array(
- 'more' => false,
- 'pages' => 1,
- ),
- );
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action Action.
- * @param int $experiment_id Experiment ID.
- * @param int $goal_index Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
- if ( 'nab_formcraft_form' !== $action['formType'] ) {
- return;
- }
- add_action(
- 'formcraft_before_save',
- function ( $form ) use ( $action, $experiment_id, $goal_index ) {
- /** @var array{'Form ID':int} $form */
-
- if ( absint( $form['Form ID'] ) !== absint( $action['formId'] ) ) {
- return;
- }
- maybe_sync_event_submission( $experiment_id, $goal_index );
- }
- );
-}
-
-add_action(
- 'plugins_loaded',
- function () {
- if ( ! function_exists( 'is_plugin_active' ) ) {
- nab_require_wp_file( '/wp-admin/includes/plugin.php' );
- }
-
- if ( ! is_plugin_active( 'formcraft-form-builder/formcraft-main.php' ) ) {
- return;
- }
-
- add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
- add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_formcraft_form', 10, 3 );
- add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_formcraft_forms', 10, 3 );
- add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
- }
-);
--- a/nelio-ab-testing/includes/hooks/compat/formidable-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/formidable-forms/index.php
@@ -1,187 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatFormidableForms;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
- $data['nab_formidable_form'] = array(
- 'name' => 'nab_formidable_form',
- 'label' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
- 'labels' => array(
- 'singular_name' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
- ),
- 'kind' => 'form',
- );
- return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post The post to filter.
- * @param int|string $post_id The id of the post.
- * @param string $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_formidable_form( $post, $post_id, $post_type ) {
- if ( null !== $post ) {
- return $post;
- }
-
- if ( 'nab_formidable_form' !== $post_type ) {
- return $post;
- }
-
- $form = FrmForm::getOne( intval( $post_id ) );
-
- if ( empty( $form ) ) {
- return new WP_Error(
- 'not-found',
- sprintf(
- /* translators: %d: Form ID. */
- _x( 'Formidable form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
- $post_id
- )
- );
- }
-
- return array(
- 'id' => absint( $post_id ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form->name,
- 'type' => 'nab_formidable_form',
- 'typeLabel' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result The result data.
- * @param string $post_type The post type.
- * @param string $query The query term.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_formidable_forms( $result, $post_type, $query ) {
- if ( null !== $result ) {
- return $result;
- }
-
- if ( 'nab_formidable_form' !== $post_type ) {
- return $result;
- }
-
- $forms = FrmForm::getAll();
- if ( ! empty( $query ) ) {
- $forms = array_values(
- array_filter(
- $forms,
- fn ( $f ) => absint( $query ) === $f->id || false !== strpos( strtolower( $f->name ), strtolower( $query ) )
- )
- );
- }
-
- $forms = array_map(
- function ( $form ) {
- return array(
- 'id' => absint( $form->id ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form->name,
- 'type' => 'nab_formidable_form',
- 'typeLabel' => _x( 'Formidable Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
- },
- $forms
- );
-
- return array(
- 'results' => $forms,
- 'pagination' => array(
- 'more' => false,
- 'pages' => 1,
- ),
- );
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action Action.
- * @param int $experiment_id Experiment ID.
- * @param int $goal_index Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
- if ( 'nab_formidable_form' !== $action['formType'] ) {
- return;
- }
- add_action(
- 'frm_after_create_entry',
- function ( $entry_id, $form_id ) use ( $action, $experiment_id, $goal_index ) {
- /** @var TIgnore $entry_id */
- /** @var int $form_id */
-
- if ( absint( $form_id ) !== absint( $action['formId'] ) ) {
- return;
- }
- maybe_sync_event_submission( $experiment_id, $goal_index );
- },
- 30,
- 2
- );
-}
-
-add_action(
- 'plugins_loaded',
- function () {
- if ( ! function_exists( 'is_plugin_active' ) ) {
- nab_require_wp_file( '/wp-admin/includes/plugin.php' );
- }
-
- if ( ! is_plugin_active( 'formidable/formidable.php' ) ) {
- return;
- }
-
- add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
- add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_formidable_form', 10, 3 );
- add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_formidable_forms', 10, 3 );
- add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
- }
-);
--- a/nelio-ab-testing/includes/hooks/compat/forminator/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forminator/index.php
@@ -1,217 +0,0 @@
-<?php
-
-namespace Nelio_AB_TestingCompatForminator;
-
-defined( 'ABSPATH' ) || exit;
-
-use function add_filter;
-use function add_action;
-use function is_plugin_active;
-
-use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
-
-/**
- * Adds a new form type.
- *
- * @param array<string,TPost_Type> $data Post types.
- *
- * @return array<string,TPost_Type>
- */
-function add_form_types( $data ) {
- $data['nab_forminator_form'] = array(
- 'name' => 'nab_forminator_form',
- 'label' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
- 'labels' => array(
- 'singular_name' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
- ),
- 'kind' => 'form',
- );
- return $data;
-}
-
-/**
- * Callback to return the appropriate form.
- *
- * @param null|TPost|WP_Post|WP_Error $post The post to filter.
- * @param int|string $post_id The id of the post.
- * @param string $post_type The post type.
- *
- * @return null|TPost|WP_Post|WP_Error
- */
-function get_forminator_form( $post, $post_id, $post_type ) {
- if ( null !== $post ) {
- return $post;
- }
-
- if ( 'nab_forminator_form' !== $post_type ) {
- return $post;
- }
-
- $form = Forminator_API::get_form( intval( $post_id ) );
- if ( is_wp_error( $form ) ) {
- return new WP_Error(
- 'not-found',
- sprintf(
- /* translators: %d: Form ID. */
- _x( 'Forminator form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
- $post_id
- )
- );
- }
-
- return array(
- 'id' => absint( $post_id ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form->settings['formName'],
- 'type' => 'nab_forminator_form',
- 'typeLabel' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
-}
-
-/**
- * Returns the list of forms matching the search query.
- *
- * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result The result data.
- * @param string $post_type The post type.
- * @param string $query The query term.
- * @param int $per_page The number of posts to show per page.
- * @param int $page The number of the current page.
- *
- * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
- */
-function search_forminator_forms( $result, $post_type, $query, $per_page, $page ) {
- if ( null !== $result ) {
- return $result;
- }
-
- if ( 'nab_forminator_form' !== $post_type ) {
- return $result;
- }
-
- if ( empty( $query ) ) {
- $forms = Forminator_API::get_forms( null, $page, $per_page );
- $forms = ! is_wp_error( $forms ) ? $forms : array();
- } elseif ( absint( $query ) ) {
- $forms = Forminator_API::get_forms( array( absint( $query ) ) );
- $forms = ! is_wp_error( $forms ) ? $forms : array();
- } else {
- $forms = Forminator_API::get_forms( null, 0, 100 );
- $forms = ! is_wp_error( $forms ) ? $forms : array();
- $forms = array_values(
- array_filter(
- $forms,
- fn ( $f ) => absint( $query ) === absint( $f->id ) || false !== strpos( strtolower( $f->settings['formName'] ), strtolower( $query ) ),
- )
- );
- }
-
- $published_forms = array_values( array_filter( $forms, fn( $f ) => 'publish' === $f->status ) );
- $published_forms = array_map(
- function ( $form ) {
- return array(
- 'id' => absint( $form->id ),
- 'author' => 0,
- 'authorName' => '',
- 'date' => false,
- 'excerpt' => '',
- 'imageId' => 0,
- 'imageSrc' => '',
- 'link' => '',
- 'status' => '',
- 'statusLabel' => '',
- 'thumbnailSrc' => '',
- 'title' => $form->settings['formName'],
- 'type' => 'nab_forminator_form',
- 'typeLabel' => _x( 'Forminator Form', 'text', 'nelio-ab-testing' ),
- 'extra' => array(),
- );
- },
- $published_forms
- );
-
- return array(
- 'results' => $published_forms,
- 'pagination' => array(
- 'more' => empty( $query ) ? count( $forms ) === $per_page : false,
- 'pages' => ! empty( $query ) || empty( $page ) ? 1 : $page,
- ),
- );
-}
-
-/**
- * Adds hooks for tracking a form submission.
- *
- * @param TForm_Submission_Attributes $action Action.
- * @param int $experiment_id Experiment ID.
- * @param int $goal_index Goal index.
- *
- * @return void
- */
-function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
- if ( 'nab_forminator_form' !== $action['formType'] ) {
- return;
- }
-
- // For non-ajax submission forms.
- add_action(
- 'forminator_form_after_handle_submit',
- function ( $form_id, $response ) use ( $action, $experiment_id, $goal_index ) {
- /** @var int $form_id */
- /** @var array{success?:bool} $response */
-
- if ( empty( $response['success'] ) ) {
- return;
- }
- if ( absint( $form_id ) !== absint( $action['formId'] ) ) {
- return;
- }
- maybe_sync_event_submission( $experiment_id, $goal_index );
- },
- 10,
- 2
- );
-
- // For ajax submission forms.
- add_action(
- 'forminator_form_after_save_entry',
- function ( $form_id, $response ) use ( $action, $experiment_id, $goal_index ) {
- if ( empty( $response ) || ! is_array( $response ) || ! $response['success'] ) {
- return;
- }
- if ( absint( $form_id ) !== absint( $action['formId'] ) ) {
- return;
- }
- maybe_sync_event_submission( $experiment_id, $goal_index );
- },
- 10,
- 2
- );
-}
-
-add_action(
- 'plugins_loaded',
- function () {
- if ( ! function_exists( 'is_plugin_active' ) ) {
- nab_require_wp_file( '/wp-admin/includes/plugin.php' );
- }
-
- if ( ! is_plugin_active( 'forminator/forminator.php' ) ) {
- return;
- }
-
- add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
- add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_forminator_form', 10, 3 );
- add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_forminator_forms', 10, 5 );
- add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
- }
-);
--- a/nelio-ab-testing/includes/hooks/compat/forms/contact-form-7/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/contact-form-7/index.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsContactForm7;
+
+defined( 'ABSPATH' ) || exit;
+
+use function add_filter;
+use function add_action;
+use function is_plugin_active;
+
+use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
+
+/**
+ * Adds a new form type.
+ *
+ * @param array<string,TPost_Type> $data Post types.
+ *
+ * @return array<string,TPost_Type>
+ */
+function add_form_types( $data ) {
+ $data['wpcf7_contact_form'] = array(
+ 'name' => 'wpcf7_contact_form',
+ 'label' => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
+ 'labels' => array(
+ 'singular_name' => _x( 'Contact Form 7', 'text', 'nelio-ab-testing' ),
+ ),
+ 'kind' => 'form',
+ );
+ return $data;
+}
+
+/**
+ * Adds hooks for tracking a form submission.
+ *
+ * @param TForm_Submission_Attributes $action Action.
+ * @param int $experiment_id Experiment ID.
+ * @param int $goal_index Goal index.
+ *
+ * @return void
+ */
+function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
+ if ( 'wpcf7_contact_form' !== $action['formType'] ) {
+ return;
+ }
+ add_action(
+ 'wpcf7_submit',
+ function ( $form, $result ) use ( $action, $experiment_id, $goal_index ) {
+ /** @var WPCF7_ContactForm $form */
+ /** @var array{status:string} $result */
+
+ if ( $action['formId'] !== $form->id() ) {
+ return;
+ }
+ if ( ! in_array( $result['status'], array( 'mail_sent', 'demo_mode' ), true ) ) {
+ return;
+ }
+ maybe_sync_event_submission( $experiment_id, $goal_index );
+ },
+ 10,
+ 2
+ );
+}
+
+add_action(
+ 'plugins_loaded',
+ function () {
+ if ( ! function_exists( 'is_plugin_active' ) ) {
+ nab_require_wp_file( '/wp-admin/includes/plugin.php' );
+ }
+
+ if ( ! is_plugin_active( 'contact-form-7/wp-contact-form-7.php' ) ) {
+ return;
+ }
+
+ add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
+ add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
+ }
+);
--- a/nelio-ab-testing/includes/hooks/compat/forms/fluent-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/fluent-forms/index.php
@@ -0,0 +1,233 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsFluentForms;
+
+defined( 'ABSPATH' ) || exit;
+
+use function add_filter;
+use function add_action;
+use function is_plugin_active;
+
+use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
+
+/**
+ * Adds a new form type.
+ *
+ * @param array<string,TPost_Type> $data Post types.
+ *
+ * @return array<string,TPost_Type>
+ */
+function add_form_types( $data ) {
+ $data['nab_fluent_form'] = array(
+ 'name' => 'nab_fluent_form',
+ 'label' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+ 'labels' => array(
+ 'singular_name' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+ ),
+ 'kind' => 'form',
+ );
+ return $data;
+}
+
+/**
+ * Callback to return the appropriate form.
+ *
+ * @param null|TPost|WP_Post|WP_Error $post The post to filter.
+ * @param int|string $post_id The id of the post.
+ * @param string $post_type The post type.
+ *
+ * @return null|TPost|WP_Post|WP_Error
+ */
+function get_fluent_form( $post, $post_id, $post_type ) {
+ if ( null !== $post ) {
+ return $post;
+ }
+
+ if ( 'nab_fluent_form' !== $post_type ) {
+ return $post;
+ }
+
+ $form = FluentFormAppModelsForm::where( 'id', $post_id )->first();
+
+ if ( empty( $form ) ) {
+ return new WP_Error(
+ 'not-found',
+ sprintf(
+ /* translators: %d: Form ID. */
+ _x( 'Fluent form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
+ $post_id
+ )
+ );
+ }
+
+ return array(
+ 'id' => absint( $form['id'] ),
+ 'author' => 0,
+ 'authorName' => '',
+ 'date' => false,
+ 'excerpt' => '',
+ 'imageId' => 0,
+ 'imageSrc' => '',
+ 'link' => '',
+ 'status' => '',
+ 'statusLabel' => '',
+ 'thumbnailSrc' => '',
+ 'title' => $form['title'],
+ 'type' => 'nab_fluent_form',
+ 'typeLabel' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+ 'extra' => array(),
+ );
+}
+
+/**
+ * Returns the list of forms matching the search query.
+ *
+ * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result The result data.
+ * @param string $post_type The post type.
+ * @param string $query The query term.
+ * @param int $per_page The number of posts to show per page.
+ * @param int $page The number of the current page.
+ *
+ * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
+ */
+function search_fluent_forms( $result, $post_type, $query, $per_page, $page ) {
+ if ( null !== $result ) {
+ return $result;
+ }
+
+ if ( 'nab_fluent_form' !== $post_type ) {
+ return $result;
+ }
+
+ $forms = FluentFormAppModelsForm::select( array( 'id', 'title', 'status' ) )
+ ->where( 'status', 'published' )
+ ->orderBy( 'title', 'DESC' )
+ ->get();
+ $forms = $forms->getDictionary();
+ $forms = array_map( fn( $f ) => $f->getAttributes(), $forms );
+ /** @var list<array{id:numeric, title:string, status:string}> */
+ $forms = array_values( $forms );
+
+ $query = strtolower( trim( $query ) );
+ if ( ! empty( $query ) ) {
+ $form_id = absint( $query );
+ $forms = array_values(
+ array_filter(
+ $forms,
+ fn( $form ) => (
+ absint( $form['id'] ) === absint( $form_id ) ||
+ false !== strpos( strtolower( $form['title'] ), $query )
+ )
+ )
+ );
+ }
+
+ $forms = array_map(
+ function ( $form ) {
+ return array(
+ 'id' => absint( $form['id'] ),
+ 'author' => 0,
+ 'authorName' => '',
+ 'date' => false,
+ 'excerpt' => '',
+ 'imageId' => 0,
+ 'imageSrc' => '',
+ 'link' => '',
+ 'status' => '',
+ 'statusLabel' => '',
+ 'thumbnailSrc' => '',
+ 'title' => $form['title'],
+ 'type' => 'nab_fluent_form',
+ 'typeLabel' => _x( 'Fluent Form', 'text', 'nelio-ab-testing' ),
+ 'extra' => array(),
+ );
+ },
+ $forms
+ );
+
+ return array(
+ 'results' => $forms,
+ 'pagination' => array(
+ 'more' => count( $forms ) === $per_page,
+ 'pages' => empty( $page ) ? 1 : $page,
+ ),
+ );
+}
+
+/**
+ * Adds hooks for tracking a form submission.
+ *
+ * @param TForm_Submission_Attributes $action Action.
+ * @param int $experiment_id Experiment ID.
+ * @param int $goal_index Goal index.
+ *
+ * @return void
+ */
+function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
+ if ( 'nab_fluent_form' !== $action['formType'] ) {
+ return;
+ }
+ add_action(
+ 'fluentform/notify_on_form_submit',
+ function ( $entry_id, $form_data, $form ) use ( $action, $experiment_id, $goal_index ) {
+ /** @var TIgnore $entry_id */
+ /** @var TIgnore $form_data */
+ /** @var FluentFormAppModelsForm $form */
+
+ if ( absint( $form->getAttributes()['id'] ) !== absint( $action['formId'] ) ) {
+ return;
+ }
+ $args = array();
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( isset( $_REQUEST['data'] ) && is_string( $_REQUEST['data'] ) ) {
+ try {
+ $args = array_reduce(
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ explode( '&', sanitize_text_field( wp_unslash( $_REQUEST['data'] ) ) ),
+ function ( $r, $i ) {
+ /** @var array<string,string> $r */
+ /** @var string $i */
+
+ $arg = explode( '=', $i );
+ $r[ $arg[0] ] = urldecode( $arg[1] );
+ return $r;
+ },
+ array()
+ );
+ } catch ( Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
+ }
+ }
+ foreach ( $args as $name => $value ) {
+ if ( 0 !== strpos( $name, 'nab_' ) ) {
+ continue;
+ }
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_REQUEST[ $name ] ) ) {
+ $_POST[ $name ] = $value;
+ $_REQUEST[ $name ] = $value;
+ }
+ }
+ maybe_sync_event_submission( $experiment_id, $goal_index );
+ },
+ 10,
+ 3
+ );
+}
+
+add_action(
+ 'plugins_loaded',
+ function () {
+ if ( ! function_exists( 'is_plugin_active' ) ) {
+ nab_require_wp_file( '/wp-admin/includes/plugin.php' );
+ }
+
+ if ( ! is_plugin_active( 'fluentform/fluentform.php' ) && ! is_plugin_active( 'fluentform-pro/fluentform-pro.php' ) ) {
+ return;
+ }
+
+ add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
+ add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_fluent_form', 10, 3 );
+ add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_fluent_forms', 10, 5 );
+ add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
+ }
+);
--- a/nelio-ab-testing/includes/hooks/compat/forms/formcraft/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/formcraft/index.php
@@ -0,0 +1,207 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsFormcraft;
+
+defined( 'ABSPATH' ) || exit;
+
+use function add_filter;
+use function add_action;
+use function is_plugin_active;
+
+use function Nelio_AB_TestingConversion_Action_LibraryForm_Submissionmaybe_sync_event_submission;
+
+/**
+ * Adds a new form type.
+ *
+ * @param array<string,TPost_Type> $data Post types.
+ *
+ * @return array<string,TPost_Type>
+ */
+function add_form_types( $data ) {
+ $data['nab_formcraft_form'] = array(
+ 'name' => 'nab_formcraft_form',
+ 'label' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+ 'labels' => array(
+ 'singular_name' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+ ),
+ 'kind' => 'form',
+ );
+ return $data;
+}
+
+/**
+ * Callback to return the appropriate form.
+ *
+ * @param null|TPost|WP_Post|WP_Error $post The post to filter.
+ * @param int|string $post_id The id of the post.
+ * @param string $post_type The post type.
+ *
+ * @return null|TPost|WP_Post|WP_Error
+ */
+function get_formcraft_form( $post, $post_id, $post_type ) {
+ if ( null !== $post ) {
+ return $post;
+ }
+
+ if ( 'nab_formcraft_form' !== $post_type ) {
+ return $post;
+ }
+
+ /** @var wpdb $wpdb */
+ global $wpdb;
+ $forms_table = $wpdb->prefix . 'formcraft_b_forms';
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+ $form = $wpdb->get_row(
+ $wpdb->prepare(
+ 'SELECT * FROM %i WHERE id=%d',
+ $forms_table,
+ $post_id
+ ),
+ ARRAY_A
+ );
+
+ if ( empty( $form ) ) {
+ return new WP_Error(
+ 'not-found',
+ sprintf(
+ /* translators: %d: Form ID. */
+ _x( 'FormCraft form with ID “%d” not found.', 'text', 'nelio-ab-testing' ),
+ $post_id
+ )
+ );
+ }
+
+ return array(
+ 'id' => absint( $post_id ),
+ 'author' => 0,
+ 'authorName' => '',
+ 'date' => false,
+ 'excerpt' => '',
+ 'imageId' => 0,
+ 'imageSrc' => '',
+ 'link' => '',
+ 'status' => '',
+ 'statusLabel' => '',
+ 'thumbnailSrc' => '',
+ 'title' => is_string( $form['name'] ) ? $form['name'] : "$post_id",
+ 'type' => 'nab_formcraft_form',
+ 'typeLabel' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+ 'extra' => array(),
+ );
+}
+
+/**
+ * Returns the list of forms matching the search query.
+ *
+ * @param null|array{results:list<TPost>, pagination: array{more:bool, pages:int}} $result The result data.
+ * @param string $post_type The post type.
+ * @param string $query The query term.
+ *
+ * @return null|array{results:list<TPost>, pagination: array{more:bool, pages:int}}
+ */
+function search_formcraft_forms( $result, $post_type, $query ) {
+ if ( null !== $result ) {
+ return $result;
+ }
+
+ if ( 'nab_formcraft_form' !== $post_type ) {
+ return $result;
+ }
+
+ /** @var wpdb $wpdb */
+ global $wpdb;
+ $forms_table = $wpdb->prefix . 'formcraft_b_forms';
+ /** @var list<object{id:int,name:string}> */
+
+ // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
+ $forms = $wpdb->get_results(
+ $wpdb->prepare(
+ 'SELECT * FROM %i',
+ $forms_table
+ )
+ );
+
+ if ( ! empty( $query ) ) {
+ $forms = array_values(
+ array_filter(
+ $forms,
+ fn( $f ) => absint( $query ) === absint( $f->id ) || false !== strpos( strtolower( $f->name ), strtolower( $query ) ),
+ )
+ );
+ }
+
+ $forms = array_map(
+ function ( $form ) {
+ return array(
+ 'id' => absint( $form->id ),
+ 'author' => 0,
+ 'authorName' => '',
+ 'date' => false,
+ 'excerpt' => '',
+ 'imageId' => 0,
+ 'imageSrc' => '',
+ 'link' => '',
+ 'status' => '',
+ 'statusLabel' => '',
+ 'thumbnailSrc' => '',
+ 'title' => $form->name,
+ 'type' => 'nab_formcraft_form',
+ 'typeLabel' => _x( 'FormCraft Form', 'text', 'nelio-ab-testing' ),
+ 'extra' => array(),
+ );
+ },
+ $forms
+ );
+
+ return array(
+ 'results' => $forms,
+ 'pagination' => array(
+ 'more' => false,
+ 'pages' => 1,
+ ),
+ );
+}
+
+/**
+ * Adds hooks for tracking a form submission.
+ *
+ * @param TForm_Submission_Attributes $action Action.
+ * @param int $experiment_id Experiment ID.
+ * @param int $goal_index Goal index.
+ *
+ * @return void
+ */
+function add_hooks_for_tracking( $action, $experiment_id, $goal_index ) {
+ if ( 'nab_formcraft_form' !== $action['formType'] ) {
+ return;
+ }
+ add_action(
+ 'formcraft_before_save',
+ function ( $form ) use ( $action, $experiment_id, $goal_index ) {
+ /** @var array{'Form ID':int} $form */
+
+ if ( absint( $form['Form ID'] ) !== absint( $action['formId'] ) ) {
+ return;
+ }
+ maybe_sync_event_submission( $experiment_id, $goal_index );
+ }
+ );
+}
+
+add_action(
+ 'plugins_loaded',
+ function () {
+ if ( ! function_exists( 'is_plugin_active' ) ) {
+ nab_require_wp_file( '/wp-admin/includes/plugin.php' );
+ }
+
+ if ( ! is_plugin_active( 'formcraft-form-builder/formcraft-main.php' ) ) {
+ return;
+ }
+
+ add_filter( 'nab_get_post_types', __NAMESPACE__ . 'add_form_types' );
+ add_filter( 'nab_pre_get_post', __NAMESPACE__ . 'get_formcraft_form', 10, 3 );
+ add_filter( 'nab_pre_get_posts', __NAMESPACE__ . 'search_formcraft_forms', 10, 3 );
+ add_action( 'nab_nab/form-submission_add_hooks_for_tracking', __NAMESPACE__ . 'add_hooks_for_tracking', 10, 3 );
+ }
+);
--- a/nelio-ab-testing/includes/hooks/compat/forms/formidable-forms/index.php
+++ b/nelio-ab-testing/includes/hooks/compat/forms/formidable-forms/index.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Nelio_AB_TestingCompatFormsFormidableForms;
+
+defined( 'AB