From b99ba0dda351cb6256420fcd7c2ea180fb60de5c Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:51:41 +0100 Subject: [PATCH 01/12] Boilerplate code. TODO: track submission. --- assets/src/js/integrations/form-submit.js | 85 +++++++++++++++++++++++ src/Admin/Settings/Page.php | 41 ++++++----- src/Integrations.php | 19 +++-- src/Integrations/FormSubmit.php | 42 +++++++++++ webpack.config.js | 3 +- 5 files changed, 166 insertions(+), 24 deletions(-) create mode 100644 assets/src/js/integrations/form-submit.js create mode 100644 src/Integrations/FormSubmit.php diff --git a/assets/src/js/integrations/form-submit.js b/assets/src/js/integrations/form-submit.js new file mode 100644 index 00000000..5137946f --- /dev/null +++ b/assets/src/js/integrations/form-submit.js @@ -0,0 +1,85 @@ +/** + * Plausible Analytics + * + * Track Form Submissions JS + */ +document.addEventListener('DOMContentLoaded', () => { + let plausible_track_form_submit = { + forms: document.querySelectorAll('form'), + + /** + * Initialization. + */ + init: function () { + this.bindEvents(); + }, + + /** + * Bind Events. + */ + bindEvents: function () { + let self = this; + + this.forms.forEach((form) => { + form.addEventListener('submit', function (e) { + if (self.isValid(e.target)) { + self.trackSubmission(); + } + }) + }) + }, + + /** + * Runs basic validation on the form. + * + * @param form + * @returns {boolean} + */ + isValid: function (form) { + let self = this; + let valid = true; + + Array.from(form).some((element) => { + if (self.isInput(element)) { + if ((element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') && element.required === true && element.value === '') { + valid = false; + + return; + } + + /** + * A known caveat in this check is, that if the first option in the SELECT is e.g. "Please make a choice", it will still pass as valid. + */ + if (element.tagName === 'SELECT' && element.required === true && element.selectedIndex < 0) { + valid = false; + + return; + } + } + }); + + return valid; + }, + + /** + * Is element an input field? + * + * @param element + * @returns {boolean} + */ + isInput: function (element) { + let inputs = ['INPUT', 'SELECT', 'TEXTAREA']; + + return inputs.includes(element.tagName); + }, + + /** + * Send a custom event to Plausible. + */ + trackSubmission: function () { + console.log('trackSubmission'); + } + }; + + plausible_track_form_submit.init(); +}); diff --git a/src/Admin/Settings/Page.php b/src/Admin/Settings/Page.php index d6b70632..29aafc2b 100644 --- a/src/Admin/Settings/Page.php +++ b/src/Admin/Settings/Page.php @@ -2,7 +2,6 @@ /** * Plausible Analytics | Settings API. - * * @since 1.3.0 * @package WordPress * @subpackage Plausible Analytics @@ -72,7 +71,6 @@ class Page extends API { /** * Constructor. - * * @since 1.3.0 * @access public * @return void @@ -120,7 +118,8 @@ public function __construct() { ], [ 'label' => empty( $settings[ 'domain_name' ] ) || empty( $settings[ 'api_token' ] ) ? - esc_html__( 'Connect', 'plausible-analytics' ) : esc_html__( 'Connected', 'plausible-analytics' ), + esc_html__( 'Connect', 'plausible-analytics' ) : + esc_html__( 'Connected', 'plausible-analytics' ), 'slug' => 'connect_plausible_analytics', 'type' => 'button', 'disabled' => empty( $settings[ 'domain_name' ] ) || @@ -190,6 +189,13 @@ public function __construct() { 'type' => 'checkbox', 'value' => 'pageview-props', ], + 'form-submit' => [ + 'label' => esc_html__( 'Form submissions', 'plausible-analytics' ), + 'docs' => '', + 'slug' => 'enhanced_measurements', + 'type' => 'checkbox', + 'value' => 'form-submit', + ], 'hash' => [ 'label' => esc_html__( 'Hash-based routing', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-hash-based-url-tracking', @@ -221,7 +227,8 @@ public function __construct() { get_site_url( null, rest_get_url_prefix() ), empty( Helpers::get_settings()[ 'proxy_enabled' ] - ) ? 'a random directory/file for storing the JS file' : 'a JS file, called ' . str_replace( + ) ? 'a random directory/file for storing the JS file' : + 'a JS file, called ' . str_replace( ABSPATH, '', Helpers::get_proxy_resource( 'cache_dir' ) . Helpers::get_proxy_resource( @@ -256,7 +263,8 @@ public function __construct() { 'slug' => 'enable_analytics_dashboard', 'type' => 'checkbox', 'value' => 'on', - 'disabled' => empty( Helpers::get_settings()[ 'api_token' ] ) && empty( Helpers::get_settings()[ 'self_hosted_domain' ] ), + 'disabled' => empty( Helpers::get_settings()[ 'api_token' ] ) && + empty( Helpers::get_settings()[ 'self_hosted_domain' ] ), ], ], ], @@ -377,15 +385,13 @@ public function __construct() { 'slug' => 'self_hosted_shared_link', 'type' => 'group', 'desc' => sprintf( - '
  1. ' . - __( + '
    1. ' . __( 'Create a secure and private shared link in your Plausible account.', 'plausible-analytics' - ) . - '
    2. ' . - __( 'Paste the shared link in the text box to view your stats in your WordPress dashboard.', 'plausible-analytics' ) . - '
    3. ' . - '
    ', + ) . '
  2. ' . __( + 'Paste the shared link in the text box to view your stats in your WordPress dashboard.', + 'plausible-analytics' + ) . '
  3. ' . '
', esc_url( 'https://plausible.io/docs/embed-dashboard' ) ), 'fields' => [ @@ -425,7 +431,6 @@ public function __construct() { /** * If proxy is enabled, or self-hosted domain has a value, display warning box. - * * @see self::proxy_warning() */ if ( Helpers::proxy_enabled() || ! empty( $settings[ 'self_hosted_domain' ] ) ) { @@ -458,7 +463,6 @@ public function __construct() { /** * Init action hooks. - * * @return void */ private function init() { @@ -509,7 +513,6 @@ private function build_user_roles_array( $slug, $disable_elements = [] ) { /** * Register Menu. - * * @since 1.0.0 * @access public * @return void @@ -579,7 +582,6 @@ public function register_menu() { /** * A little hack to add some classes to the core #wpcontent div. - * * @return void */ public function add_background_color() { @@ -590,7 +592,6 @@ public function add_background_color() { /** * Statistics Page via Embed feature. - * * @since 1.2.0 * @access public * @return void @@ -643,7 +644,6 @@ public function render_analytics_dashboard() { * When this option was saved to the database, underlying code would fail, throwing a CORS related error in browsers. * Now, we explicitly check for the existence of this example "auth" key, and display a human-readable error message to * those who haven't properly set it up. - * * @since v1.2.5 * For self-hosters the View Stats option doesn't need to be enabled, if a Shared Link is entered, we can assume they want to View Stats. * For regular users, the shared link is provisioned by the API, so it shouldn't be empty. @@ -707,7 +707,10 @@ public function render_analytics_dashboard() { ); ?> click here to enable View Stats in WordPress.', 'plausible-analytics' ), + __( + 'Please click here to enable View Stats in WordPress.', + 'plausible-analytics' + ), admin_url( 'options-general.php?page=plausible_analytics#is_shared_link' ) ); ?> diff --git a/src/Integrations.php b/src/Integrations.php index 97d22bd3..d0458e6e 100644 --- a/src/Integrations.php +++ b/src/Integrations.php @@ -2,7 +2,6 @@ /** * Plausible Analytics | Integrations - * * @since 2.1.0 * @package WordPress * @subpackage Plausible Analytics @@ -25,7 +24,6 @@ public function __construct() { /** * Run available integrations. - * * @return void */ private function init() { @@ -38,11 +36,14 @@ private function init() { if ( self::is_edd_active() ) { // new Integrations\EDD(); } + + if ( self::is_form_submit_active() ) { + new Integrations\FormSubmit(); + } } /** * Checks if WooCommerce is installed and activated. - * * @return bool */ public static function is_wc_active() { @@ -51,10 +52,20 @@ public static function is_wc_active() { /** * Checks if Easy Digital Downloads is installed and activated. - * * @return bool */ public static function is_edd_active() { return apply_filters( 'plausible_analytics_integrations_edd', function_exists( 'EDD' ) ); } + + /** + * Check if Form Submissions option is enabled in Enhanced Measurements. + * @return mixed|null + */ + public static function is_form_submit_active() { + return apply_filters( + 'plausible_analytics_integrations_form_submit', + Helpers::is_enhanced_measurement_enabled( 'form-submit' ) + ); + } } diff --git a/src/Integrations/FormSubmit.php b/src/Integrations/FormSubmit.php new file mode 100644 index 00000000..2fb32828 --- /dev/null +++ b/src/Integrations/FormSubmit.php @@ -0,0 +1,42 @@ +init(); + } + + /** + * Init + * @return void + */ + private function init() { + /** + * Adds required JS and classes. + */ + add_action( 'wp_enqueue_scripts', [ $this, 'add_js' ], 1 ); + } + + /** + * Enqueues the required JavaScript for form submissions integration. + * @return void + */ + public function add_js() { + wp_enqueue_script( + 'plausible-form-submit-integration', + PLAUSIBLE_ANALYTICS_PLUGIN_URL . 'assets/dist/js/plausible-form-submit-integration.js', + [], + filemtime( PLAUSIBLE_ANALYTICS_PLUGIN_DIR . 'assets/dist/js/plausible-form-submit-integration.js' ) + ); + } +} diff --git a/webpack.config.js b/webpack.config.js index 1949692e..585f156c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,7 +13,8 @@ const config = { mode, entry: { 'plausible-admin': ['./assets/src/css/admin/main.css', './assets/src/js/admin/main.js'], - 'plausible-woocommerce-integration': ['./assets/src/js/integrations/woocommerce.js'] + 'plausible-woocommerce-integration': ['./assets/src/js/integrations/woocommerce.js'], + 'plausible-form-submit-integration': ['./assets/src/js/integrations/form-submit.js'] }, output: { path: path.join(__dirname, './assets/dist/'), From b4b5d4004f443819750c93f1b0b85f3dc6b6aecb Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 17:18:05 +0100 Subject: [PATCH 02/12] Use shorthand. --- assets/src/js/integrations/form-submit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/src/js/integrations/form-submit.js b/assets/src/js/integrations/form-submit.js index 5137946f..4d3b245e 100644 --- a/assets/src/js/integrations/form-submit.js +++ b/assets/src/js/integrations/form-submit.js @@ -21,7 +21,7 @@ document.addEventListener('DOMContentLoaded', () => { let self = this; this.forms.forEach((form) => { - form.addEventListener('submit', function (e) { + form.addEventListener('submit', (e) => { if (self.isValid(e.target)) { self.trackSubmission(); } From c615ecc4a988e5f8402cffb84152272e75d9cf60 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 18:45:00 +0100 Subject: [PATCH 03/12] Fixed: this works with most plugins, except CF7. --- assets/src/js/integrations/form-submit.js | 46 +---------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/assets/src/js/integrations/form-submit.js b/assets/src/js/integrations/form-submit.js index 4d3b245e..01026597 100644 --- a/assets/src/js/integrations/form-submit.js +++ b/assets/src/js/integrations/form-submit.js @@ -22,57 +22,13 @@ document.addEventListener('DOMContentLoaded', () => { this.forms.forEach((form) => { form.addEventListener('submit', (e) => { - if (self.isValid(e.target)) { + if (e.target.checkValidity()) { self.trackSubmission(); } }) }) }, - /** - * Runs basic validation on the form. - * - * @param form - * @returns {boolean} - */ - isValid: function (form) { - let self = this; - let valid = true; - - Array.from(form).some((element) => { - if (self.isInput(element)) { - if ((element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') && element.required === true && element.value === '') { - valid = false; - - return; - } - - /** - * A known caveat in this check is, that if the first option in the SELECT is e.g. "Please make a choice", it will still pass as valid. - */ - if (element.tagName === 'SELECT' && element.required === true && element.selectedIndex < 0) { - valid = false; - - return; - } - } - }); - - return valid; - }, - - /** - * Is element an input field? - * - * @param element - * @returns {boolean} - */ - isInput: function (element) { - let inputs = ['INPUT', 'SELECT', 'TEXTAREA']; - - return inputs.includes(element.tagName); - }, - /** * Send a custom event to Plausible. */ From e19bdf3e0fa5587dd051f7daece8e3b460d6fb0c Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 19:37:03 +0100 Subject: [PATCH 04/12] Fixed: track Form Submission. --- assets/src/js/integrations/form-submit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/src/js/integrations/form-submit.js b/assets/src/js/integrations/form-submit.js index 01026597..89140add 100644 --- a/assets/src/js/integrations/form-submit.js +++ b/assets/src/js/integrations/form-submit.js @@ -33,7 +33,7 @@ document.addEventListener('DOMContentLoaded', () => { * Send a custom event to Plausible. */ trackSubmission: function () { - console.log('trackSubmission'); + plausible('Form Submission', {'props': {'form': document.location.pathname}}); } }; From 1289e97e0f16cd809ae0ec5270e7a2743794e0d1 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 19:51:26 +0100 Subject: [PATCH 05/12] Improved: make Goal Name "Form Completions" translatable. --- assets/src/js/integrations/form-submit.js | 2 +- src/Actions.php | 29 +++++++++++++---------- src/Integrations/FormSubmit.php | 12 ++++++++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/assets/src/js/integrations/form-submit.js b/assets/src/js/integrations/form-submit.js index 89140add..7b6f5a34 100644 --- a/assets/src/js/integrations/form-submit.js +++ b/assets/src/js/integrations/form-submit.js @@ -33,7 +33,7 @@ document.addEventListener('DOMContentLoaded', () => { * Send a custom event to Plausible. */ trackSubmission: function () { - plausible('Form Submission', {'props': {'form': document.location.pathname}}); + plausible(plausible_analytics_i18n.form_completions, {'props': {'form': document.location.pathname}}); } }; diff --git a/src/Actions.php b/src/Actions.php index e29b98d5..10fafb8d 100644 --- a/src/Actions.php +++ b/src/Actions.php @@ -1,7 +1,6 @@ tag "tells" the Plausible API which version of the plugin is used, to allow tailored error messages, specific to the plugin - * version. - * + * This tag "tells" the Plausible API which version of the plugin is used, to allow tailored error messages, + * specific to the plugin version. * @return void */ public function insert_version_meta_tag() { @@ -37,7 +34,6 @@ public function insert_version_meta_tag() { /** * Register Assets. - * * @since 1.0.0 * @access public * @return void @@ -56,9 +52,16 @@ public function maybe_register_assets() { } $version = - Helpers::proxy_enabled() && file_exists( Helpers::get_js_path() ) ? filemtime( Helpers::get_js_path() ) : PLAUSIBLE_ANALYTICS_VERSION; + Helpers::proxy_enabled() && file_exists( Helpers::get_js_path() ) ? filemtime( Helpers::get_js_path() ) : + PLAUSIBLE_ANALYTICS_VERSION; - wp_enqueue_script( 'plausible-analytics', Helpers::get_js_url( true ), '', $version, apply_filters( 'plausible_load_js_in_footer', false ) ); + wp_enqueue_script( + 'plausible-analytics', + Helpers::get_js_url( true ), + '', + $version, + apply_filters( 'plausible_load_js_in_footer', false ) + ); // Goal tracking inline script (Don't disable this as it is required by 404). wp_add_inline_script( @@ -77,7 +80,7 @@ public function maybe_register_assets() { ); /** - * Documentation.location.pathname is a variable. @see wp_json_encode() doesn't allow passing variable, only strings. This fixes that. + * document.location.pathname is a variable. @see wp_json_encode() doesn't allow passing variable, only strings. This fixes that. */ $data = str_replace( '"document.location.pathname"', 'document.location.pathname', $data ); @@ -95,14 +98,17 @@ public function maybe_register_assets() { [ 'props' => [ // convert queries to lowercase and remove trailing whitespace to ensure same terms are grouped together - 'search_query' => strtolower(trim(get_search_query())), + 'search_query' => strtolower( trim( get_search_query() ) ), 'result_count' => $wp_query->found_posts, ], ] ); $script = "plausible('WP Search Queries', $data );"; - wp_add_inline_script( 'plausible-analytics', "document.addEventListener('DOMContentLoaded', function() {\n$script\n});" ); + wp_add_inline_script( + 'plausible-analytics', + "document.addEventListener('DOMContentLoaded', function() {\n$script\n});" + ); } // This action allows you to add your own custom scripts! @@ -111,7 +117,6 @@ public function maybe_register_assets() { /** * Create admin bar nodes. - * * @since 1.3.0 * @access public * diff --git a/src/Integrations/FormSubmit.php b/src/Integrations/FormSubmit.php index 2fb32828..210623c8 100644 --- a/src/Integrations/FormSubmit.php +++ b/src/Integrations/FormSubmit.php @@ -32,11 +32,19 @@ private function init() { * @return void */ public function add_js() { - wp_enqueue_script( + wp_register_script( 'plausible-form-submit-integration', PLAUSIBLE_ANALYTICS_PLUGIN_URL . 'assets/dist/js/plausible-form-submit-integration.js', - [], + [ 'plausible-analytics' ], filemtime( PLAUSIBLE_ANALYTICS_PLUGIN_DIR . 'assets/dist/js/plausible-form-submit-integration.js' ) ); + + wp_localize_script( + 'plausible-form-submit-integration', + 'plausible_analytics_i18n', + [ 'form_completions' => __( 'Form Completions', 'plausible-analytics' ), ] + ); + + wp_enqueue_script( 'plausible-form-submit-integration' ); } } From 205ef84841ddd816bce539443e3c62b88700fc26 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 19:59:30 +0100 Subject: [PATCH 06/12] Added: create form custom property and Form Completions goal when option is enabled. --- src/Admin/Provisioning.php | 38 ++++++++++++++++++------------------- src/Admin/Settings/Page.php | 24 +++++++++++------------ 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/Admin/Provisioning.php b/src/Admin/Provisioning.php index 424d2ab8..ad8f1b4a 100644 --- a/src/Admin/Provisioning.php +++ b/src/Admin/Provisioning.php @@ -1,7 +1,6 @@ custom_event_goals = [ - '404' => __( '404', 'plausible-analytics' ), - 'outbound-links' => __( 'Outbound Link: Click', 'plausible-analytics' ), - 'file-downloads' => __( 'File Download', 'plausible-analytics' ), - 'search' => __( 'WP Search Queries', 'plausible-analytics' ), + '404' => __( '404', 'plausible-analytics' ), + 'outbound-links' => __( 'Outbound Link: Click', 'plausible-analytics' ), + 'file-downloads' => __( 'File Download', 'plausible-analytics' ), + 'search' => __( 'WP Search Queries', 'plausible-analytics' ), + 'form-completions' => __( 'Form Completions', 'plausible-analytics' ), ]; $this->init(); @@ -79,10 +79,8 @@ public function __construct( $client = null ) { /** * Action & filter hooks. - * * @return void * @throws ApiException - * * @codeCoverageIgnore */ private function init() { @@ -210,11 +208,11 @@ private function create_goals( $goals ) { * @param $settings * * @return void - * * @codeCoverageIgnore Because we don't want to test the API. */ public function maybe_create_woocommerce_funnel( $old_settings, $settings ) { - if ( ! Helpers::is_enhanced_measurement_enabled( 'revenue', $settings[ 'enhanced_measurements' ] ) || ! Integrations::is_wc_active() ) { + if ( ! Helpers::is_enhanced_measurement_enabled( 'revenue', $settings[ 'enhanced_measurements' ] ) || + ! Integrations::is_wc_active() ) { return; // @codeCoverageIgnore } @@ -254,7 +252,6 @@ public function maybe_create_woocommerce_funnel( $old_settings, $settings ) { * @param $steps * * @return void - * * @codeCoverageIgnore Because this method should be mocked in tests if needed. */ private function create_funnel( $name, $steps ) { @@ -325,14 +322,13 @@ public function maybe_delete_goals( $old_settings, $settings ) { } /** - * Delete all custom WooCommerce event goals if Revenue setting is disabled. The funnel is deleted when the minimum required no. of goals is no - * longer met. + * Delete all custom WooCommerce event goals if Revenue setting is disabled. The funnel is deleted when the minimum + * required no. of goals is no longer met. * * @param $old_settings * @param $settings * * @return void - * * @codeCoverageIgnore Because we don't want to test if the API is working. */ public function maybe_delete_woocommerce_goals( $old_settings, $settings ) { @@ -361,14 +357,13 @@ public function maybe_delete_woocommerce_goals( $old_settings, $settings ) { } /** - * Searches an array for the presence of $string within each element's value. Strips currencies using a regex, e.g. (USD), because these are - * added to revenue goals by Plausible. + * Searches an array for the presence of $string within each element's value. Strips currencies using a regex, e.g. + * (USD), because these are added to revenue goals by Plausible. * * @param string $string * @param array $haystack * * @return false|mixed - * * @codeCoverageIgnore Because it can't be unit tested. */ private function array_search_contains( $string, $haystack ) { @@ -390,7 +385,6 @@ private function array_search_contains( $string, $haystack ) { * @param array $settings * * @return void - * * @codeCoverageIgnore Because we don't want to test if the API is working. */ public function maybe_create_custom_properties( $old_settings, $settings ) { @@ -398,7 +392,8 @@ public function maybe_create_custom_properties( $old_settings, $settings ) { if ( ! Helpers::is_enhanced_measurement_enabled( 'pageview-props', $enhanced_measurements ) && ! Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) && - ! Helpers::is_enhanced_measurement_enabled( 'search', $enhanced_measurements ) ) { + ! Helpers::is_enhanced_measurement_enabled( 'search', $enhanced_measurements ) && + ! Helpers::is_enhanced_measurement_enabled( 'form-completions', $enhanced_measurements ) ) { return; // @codeCoverageIgnore } @@ -417,7 +412,8 @@ public function maybe_create_custom_properties( $old_settings, $settings ) { /** * Create Custom Properties for WooCommerce integration. */ - if ( Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) && Integrations::is_wc_active() ) { + if ( Helpers::is_enhanced_measurement_enabled( 'revenue', $enhanced_measurements ) && + Integrations::is_wc_active() ) { foreach ( WooCommerce::CUSTOM_PROPERTIES as $property ) { $properties[] = new Client\Model\CustomProp( [ 'custom_prop' => [ 'key' => $property ] ] ); } @@ -432,6 +428,10 @@ public function maybe_create_custom_properties( $old_settings, $settings ) { } } + if ( Helpers::is_enhanced_measurement_enabled( 'form-completions', $enhanced_measurements ) ) { + $properties[] = new Client\Model\CustomProp( [ 'custom_prop' => [ 'key' => 'form' ] ] ); + } + if ( empty( $properties ) ) { return; // @codeCoverageIgnore } diff --git a/src/Admin/Settings/Page.php b/src/Admin/Settings/Page.php index 29aafc2b..baa26c3e 100644 --- a/src/Admin/Settings/Page.php +++ b/src/Admin/Settings/Page.php @@ -139,42 +139,42 @@ public function __construct() { 'plausible-analytics' ), 'fields' => [ - '404' => [ + '404' => [ 'label' => esc_html__( '404 error pages', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-404-error-pages', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => '404', ], - 'outbound-links' => [ + 'outbound-links' => [ 'label' => esc_html__( 'Outbound links', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-external-link-clicks', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'outbound-links', ], - 'file-downloads' => [ + 'file-downloads' => [ 'label' => esc_html__( 'File downloads', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-file-downloads', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'file-downloads', ], - 'search' => [ + 'search' => [ 'label' => esc_html__( 'Search queries', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-site-search-tracking', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'search', ], - 'tagged-events' => [ + 'tagged-events' => [ 'label' => esc_html__( 'Custom events', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-setup-custom-events-to-track-goal-conversions', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'tagged-events', ], - 'revenue' => [ + 'revenue' => [ 'label' => esc_html__( 'Ecommerce revenue', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-ecommerce-revenue', 'slug' => 'enhanced_measurements', @@ -182,28 +182,28 @@ public function __construct() { 'value' => 'revenue', 'disabled' => ! empty( $settings[ 'self_hosted_domain' ] ), ], - 'pageview-props' => [ + 'pageview-props' => [ 'label' => esc_html__( 'Authors and categories', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-send-custom-properties', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'pageview-props', ], - 'form-submit' => [ - 'label' => esc_html__( 'Form submissions', 'plausible-analytics' ), + 'form-completions' => [ + 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), 'docs' => '', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', - 'value' => 'form-submit', + 'value' => 'form-completions', ], - 'hash' => [ + 'hash' => [ 'label' => esc_html__( 'Hash-based routing', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-enable-hash-based-url-tracking', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'hash', ], - 'compat' => [ + 'compat' => [ 'label' => esc_html__( 'IE compatibility', 'plausible-analytics' ), 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-visitors-who-use-internet-explorer', 'slug' => 'enhanced_measurements', From 2e1a083f67e8a61cd7702418e1c1522d0114b4ad Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:10:31 +0100 Subject: [PATCH 07/12] Fix automated tests --- .github/workflows/push.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 38ef16f5..afb2b5fb 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -40,6 +40,9 @@ jobs: --health-retries 10 steps: + - name: Install SVN + - run: sudo apt-get install -y subversion + - name: Checkout code uses: actions/checkout@v4 From d60726b7f4a49b10f2a985bc4957723e3e67632c Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:48:12 +0100 Subject: [PATCH 08/12] Added: server-side compatibility fix for CF7 --- src/Integrations/FormSubmit.php | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/Integrations/FormSubmit.php b/src/Integrations/FormSubmit.php index 210623c8..0735c0c9 100644 --- a/src/Integrations/FormSubmit.php +++ b/src/Integrations/FormSubmit.php @@ -8,6 +8,8 @@ namespace Plausible\Analytics\WP\Integrations; +use Plausible\Analytics\WP\Proxy; + class FormSubmit { /** * Build class. @@ -25,6 +27,10 @@ private function init() { * Adds required JS and classes. */ add_action( 'wp_enqueue_scripts', [ $this, 'add_js' ], 1 ); + /** + * Contact Form 7 doesn't respect JS checkValidity() function. + */ + add_filter( 'wpcf7_validate', [ $this, 'wpcf7_validate' ], 10, 2 ); } /** @@ -32,6 +38,10 @@ private function init() { * @return void */ public function add_js() { + if ( defined( 'WPCF7_VERSION' ) ) { + return; + } + wp_register_script( 'plausible-form-submit-integration', PLAUSIBLE_ANALYTICS_PLUGIN_URL . 'assets/dist/js/plausible-form-submit-integration.js', @@ -47,4 +57,31 @@ public function add_js() { wp_enqueue_script( 'plausible-form-submit-integration' ); } + + /** + * Tracks the form submission if form is valid. + * + * @param \WPCF7_Validation $result Form submission result object containing validation results. + * @param array $tags Array of tags associated with the form fields. + * + * @return \WPCF7_Validation + */ + public function wpcf7_validate( $result, $tags ) { + $invalid_fields = $result->get_invalid_fields(); + + if ( empty( $invalid_fields ) ) { + $post = get_post( $_POST[ '_wpcf7_container_post' ] ); + $uri = '/' . $post->post_name . '/'; + + $proxy = new Proxy( false ); + $proxy->do_request( + __( 'Form Completions', 'plausible-analytics' ), + null, + null, + [ 'form' => $uri ] + ); + } + + return $result; + } } From e9f55f98cd3fbd92852525230f28b809b56b74b7 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:49:41 +0100 Subject: [PATCH 09/12] Updated PHP Doc. --- src/Integrations/FormSubmit.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Integrations/FormSubmit.php b/src/Integrations/FormSubmit.php index 0735c0c9..bb6b6f74 100644 --- a/src/Integrations/FormSubmit.php +++ b/src/Integrations/FormSubmit.php @@ -28,9 +28,9 @@ private function init() { */ add_action( 'wp_enqueue_scripts', [ $this, 'add_js' ], 1 ); /** - * Contact Form 7 doesn't respect JS checkValidity() function. + * Contact Form 7 doesn't respect JS checkValidity() function, so this is a custom compatibility fix. */ - add_filter( 'wpcf7_validate', [ $this, 'wpcf7_validate' ], 10, 2 ); + add_filter( 'wpcf7_validate', [ $this, 'maybe_track_submission' ], 10, 2 ); } /** @@ -59,14 +59,14 @@ public function add_js() { } /** - * Tracks the form submission if form is valid. + * Tracks the form submission if CF7 says it's valid. * * @param \WPCF7_Validation $result Form submission result object containing validation results. * @param array $tags Array of tags associated with the form fields. * * @return \WPCF7_Validation */ - public function wpcf7_validate( $result, $tags ) { + public function maybe_track_submission( $result, $tags ) { $invalid_fields = $result->get_invalid_fields(); if ( empty( $invalid_fields ) ) { From c8c31ec518a098299314323af2b79c0e747cd2df Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 20:51:21 +0100 Subject: [PATCH 10/12] Oops. --- .github/workflows/push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index afb2b5fb..8797932b 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Install SVN - - run: sudo apt-get install -y subversion + run: sudo apt-get install -y subversion - name: Checkout code uses: actions/checkout@v4 From c30753a23406464612cba468d51e108060dca8ab Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Wed, 22 Jan 2025 21:27:03 +0100 Subject: [PATCH 11/12] Ignore code, because there's nothing we can unit/integration test. --- src/Integrations/FormSubmit.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Integrations/FormSubmit.php b/src/Integrations/FormSubmit.php index bb6b6f74..af2e6796 100644 --- a/src/Integrations/FormSubmit.php +++ b/src/Integrations/FormSubmit.php @@ -13,6 +13,7 @@ class FormSubmit { /** * Build class. + * @codeCoverageIgnore */ public function __construct() { $this->init(); @@ -21,6 +22,7 @@ public function __construct() { /** * Init * @return void + * @codeCoverageIgnore */ private function init() { /** @@ -36,6 +38,7 @@ private function init() { /** * Enqueues the required JavaScript for form submissions integration. * @return void + * @codeCoverageIgnore because there's nothing to test here. */ public function add_js() { if ( defined( 'WPCF7_VERSION' ) ) { @@ -65,6 +68,7 @@ public function add_js() { * @param array $tags Array of tags associated with the form fields. * * @return \WPCF7_Validation + * @codeCoverageIgnore because we can't test XHR requests here. */ public function maybe_track_submission( $result, $tags ) { $invalid_fields = $result->get_invalid_fields(); From b8342c0a9fc2f078b169cb54c03a1dc2ab645664 Mon Sep 17 00:00:00 2001 From: Dan0sz <18595395+Dan0sz@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:05:16 +0100 Subject: [PATCH 12/12] Added: link to docs for Form Completions option. --- src/Admin/Settings/Page.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Admin/Settings/Page.php b/src/Admin/Settings/Page.php index baa26c3e..742e570c 100644 --- a/src/Admin/Settings/Page.php +++ b/src/Admin/Settings/Page.php @@ -191,7 +191,7 @@ public function __construct() { ], 'form-completions' => [ 'label' => esc_html__( 'Form completions', 'plausible-analytics' ), - 'docs' => '', + 'docs' => 'https://plausible.io/wordpress-analytics-plugin#how-to-track-form-completions', 'slug' => 'enhanced_measurements', 'type' => 'checkbox', 'value' => 'form-completions',