diff --git a/assets/js/mailchimp.js b/assets/js/mailchimp.js index 753c8ab..5b172fc 100644 --- a/assets/js/mailchimp.js +++ b/assets/js/mailchimp.js @@ -109,3 +109,31 @@ }); } })(window.jQuery); + +/* Form view tracking for analytics */ +(function () { + if (!window.mailchimpSF || !window.mailchimpSF.analytics_ajax_url) { + return; + } + + const forms = document.querySelectorAll('.mc_signup_form[data-list-id]'); + const tracked = {}; + + for (let i = 0; i < forms.length; i++) { + const listId = forms[i].getAttribute('data-list-id'); + if (listId && !tracked[listId]) { + tracked[listId] = true; + + const formData = new FormData(); + formData.append('action', 'mailchimp_sf_track_form_view'); + formData.append('list_id', listId); + formData.append('nonce', window.mailchimpSF.analytics_nonce); + + fetch(window.mailchimpSF.analytics_ajax_url, { + method: 'POST', + body: formData, + credentials: 'same-origin', + }); + } + } +})(); diff --git a/includes/admin/templates/analytics.php b/includes/admin/templates/analytics.php index f523fce..8691ecb 100644 --- a/includes/admin/templates/analytics.php +++ b/includes/admin/templates/analytics.php @@ -84,9 +84,19 @@
-
-

-
+ get_totals( $current_list, $start_date, $end_date ); + $daily = $analytics_data->get_analytics_data( $current_list, $start_date, $end_date ); + ?> +

+
+ +

+
diff --git a/includes/blocks/mailchimp/markup.php b/includes/blocks/mailchimp/markup.php index 2c9a8a8..60e0ba1 100644 --- a/includes/blocks/mailchimp/markup.php +++ b/includes/blocks/mailchimp/markup.php @@ -105,7 +105,7 @@ function ( $single_list ) { } ?>
-
+ diff --git a/includes/class-mailchimp-analytics-data.php b/includes/class-mailchimp-analytics-data.php new file mode 100644 index 0000000..228f197 --- /dev/null +++ b/includes/class-mailchimp-analytics-data.php @@ -0,0 +1,218 @@ +prefix . 'mailchimp_sf_form_analytics'; + } + + /** + * Create the analytics table. + */ + public static function create_table() { + global $wpdb; + + $table_name = self::get_table_name(); + $charset_collate = $wpdb->get_charset_collate(); + + $sql = "CREATE TABLE {$table_name} ( + id bigint(20) unsigned NOT NULL AUTO_INCREMENT, + list_id varchar(20) NOT NULL, + form_id varchar(50) NOT NULL DEFAULT '', + event_date date NOT NULL, + views bigint(20) unsigned NOT NULL DEFAULT 0, + submissions bigint(20) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (id), + UNIQUE KEY list_form_date (list_id, form_id, event_date) + ) {$charset_collate};"; + + require_once ABSPATH . 'wp-admin/includes/upgrade.php'; + dbDelta( $sql ); + + update_option( 'mailchimp_sf_analytics_db_version', self::DB_VERSION ); + } + + /** + * Increment the view count for a list on today's date. + * + * @param string $list_id The list ID. + * @param string $form_id The form ID. + */ + public function increment_views( $list_id, $form_id = '' ) { + global $wpdb; + + $table_name = self::get_table_name(); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wpdb->query( + $wpdb->prepare( + "INSERT INTO {$table_name} (list_id, form_id, event_date, views, submissions) + VALUES (%s, %s, %s, 1, 0) + ON DUPLICATE KEY UPDATE views = views + 1", + $list_id, + $form_id, + current_time( 'Y-m-d' ) + ) + ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + } + + /** + * Increment the submission count for a list on today's date. + * + * @param string $list_id The list ID. + * @param string $form_id The form ID. + */ + public function increment_submissions( $list_id, $form_id = '' ) { + global $wpdb; + + $table_name = self::get_table_name(); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $wpdb->query( + $wpdb->prepare( + "INSERT INTO {$table_name} (list_id, form_id, event_date, views, submissions) + VALUES (%s, %s, %s, 0, 1) + ON DUPLICATE KEY UPDATE submissions = submissions + 1", + $list_id, + $form_id, + current_time( 'Y-m-d' ) + ) + ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + } + + /** + * Get analytics data for a list within a date range. + * + * @param string $list_id The list ID. + * @param string $start_date Start date (Y-m-d). + * @param string $end_date End date (Y-m-d). + * @return array Array of daily analytics rows. + */ + public function get_analytics_data( $list_id, $start_date, $end_date ) { + global $wpdb; + + $table_name = self::get_table_name(); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $results = $wpdb->get_results( + $wpdb->prepare( + "SELECT event_date, SUM(views) AS views, SUM(submissions) AS submissions + FROM {$table_name} + WHERE list_id = %s AND event_date BETWEEN %s AND %s + GROUP BY event_date + ORDER BY event_date ASC", + $list_id, + $start_date, + $end_date + ), + ARRAY_A + ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + + return $results; + } + + /** + * Get totals for a list within a date range. + * + * @param string $list_id The list ID. + * @param string $start_date Start date (Y-m-d). + * @param string $end_date End date (Y-m-d). + * @return array Associative array with total views and submissions. + */ + public function get_totals( $list_id, $start_date, $end_date ) { + global $wpdb; + + $table_name = self::get_table_name(); + + // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + $result = $wpdb->get_row( + $wpdb->prepare( + "SELECT COALESCE(SUM(views), 0) AS total_views, COALESCE(SUM(submissions), 0) AS total_submissions + FROM {$table_name} + WHERE list_id = %s AND event_date BETWEEN %s AND %s", + $list_id, + $start_date, + $end_date + ), + ARRAY_A + ); + // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared + + if ( ! $result ) { + return array( + 'total_views' => 0, + 'total_submissions' => 0, + ); + } + + return $result; + } + + /** + * Handle the AJAX form view tracking request. + */ + public function handle_form_view() { + // Verify nonce. + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'mailchimp_sf_analytics_nonce' ) ) { + wp_send_json_error( 'Invalid nonce.', 403 ); + } + + $list_id = isset( $_POST['list_id'] ) ? sanitize_text_field( wp_unslash( $_POST['list_id'] ) ) : ''; + + if ( empty( $list_id ) ) { + wp_send_json_error( 'Missing list_id.', 400 ); + } + + $this->increment_views( $list_id ); + wp_send_json_success(); + } + + /** + * Track a successful form submission. + * + * @param string $list_id The list ID. + */ + public function track_submission( $list_id ) { + if ( ! empty( $list_id ) ) { + $this->increment_submissions( $list_id ); + } + } +} diff --git a/includes/class-mailchimp-analytics.php b/includes/class-mailchimp-analytics.php index 7fd5eed..e077175 100644 --- a/includes/class-mailchimp-analytics.php +++ b/includes/class-mailchimp-analytics.php @@ -97,6 +97,5 @@ public function enqueue_scripts( $hook_suffix ) { MCSF_VER, true ); - } } diff --git a/includes/class-mailchimp-form-submission.php b/includes/class-mailchimp-form-submission.php index 68d8278..934a592 100644 --- a/includes/class-mailchimp-form-submission.php +++ b/includes/class-mailchimp-form-submission.php @@ -176,6 +176,13 @@ public function handle_form_submission() { $message = __( 'Success, you\'ve been signed up! Please look for our confirmation email.', 'mailchimp' ); } + /** + * Fires after a successful form submission. + * + * @param string $list_id The list ID the user subscribed to. + */ + do_action( 'mailchimp_sf_form_submission_success', $list_id ); + // Return success message. return $message; } diff --git a/mailchimp.php b/mailchimp.php index ea07331..6421999 100644 --- a/mailchimp.php +++ b/mailchimp.php @@ -115,6 +115,14 @@ function () { $analytics = new Mailchimp_Analytics(); $analytics->init(); +// Analytics data class. +require_once plugin_dir_path( __FILE__ ) . 'includes/class-mailchimp-analytics-data.php'; +$analytics_data = new Mailchimp_Analytics_Data(); +$analytics_data->init(); + +// Create analytics table on activation. +register_activation_hook( __FILE__, array( 'Mailchimp_Analytics_Data', 'create_table' ) ); + // Deprecated functions. require_once plugin_dir_path( __FILE__ ) . 'includes/mailchimp-deprecated-functions.php'; @@ -171,6 +179,8 @@ function mailchimp_sf_load_resources() { array( 'ajax_url' => trailingslashit( home_url() ), 'phone_validation_error' => esc_html__( 'Please enter a valid phone number.', 'mailchimp' ), + 'analytics_ajax_url' => admin_url( 'admin-ajax.php' ), + 'analytics_nonce' => wp_create_nonce( 'mailchimp_sf_analytics_nonce' ), ) ); diff --git a/mailchimp_upgrade.php b/mailchimp_upgrade.php index debe2b9..9985a5c 100644 --- a/mailchimp_upgrade.php +++ b/mailchimp_upgrade.php @@ -26,6 +26,12 @@ function mailchimp_version_check() { mailchimp_update_1_7_0(); } + // Create analytics table if it doesn't exist. + $analytics_db_version = get_option( 'mailchimp_sf_analytics_db_version' ); + if ( false === $analytics_db_version || version_compare( Mailchimp_Analytics_Data::DB_VERSION, $analytics_db_version, '>' ) ) { + Mailchimp_Analytics_Data::create_table(); + } + update_option( 'mc_version', MCSF_VER ); } diff --git a/mailchimp_widget.php b/mailchimp_widget.php index 3ef5ced..3e2b270 100644 --- a/mailchimp_widget.php +++ b/mailchimp_widget.php @@ -92,7 +92,7 @@ function mailchimp_sf_signup_form( $args = array() ) { ?>
- +