prefix . 'ftcon_form_entries'; $days = self::DAYS; $to = get_option( 'ftcon_notification_email', get_option( 'admin_email' ) ); if ( empty( $to ) || ! is_email( $to ) ) { return; } // Current period entries. $date_from = gmdate( 'Y-m-d H:i:s', strtotime( "-{$days} days" ) ); $entries = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table} WHERE submitted_at >= %s ORDER BY submitted_at DESC", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $date_from ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $total = count( $entries ); // Previous period entries (for comparison). $prev_from = gmdate( 'Y-m-d H:i:s', strtotime( '-' . ( $days * 2 ) . ' days' ) ); $prev_total = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} WHERE submitted_at >= %s AND submitted_at < %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $prev_from, $date_from ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching // Traffic source breakdown. $source_counts = array(); foreach ( $entries as $entry ) { $src = ! empty( $entry->traffic_source ) ? $entry->traffic_source : __( 'Direct', 'forge-trueconversion' ); if ( ! isset( $source_counts[ $src ] ) ) { $source_counts[ $src ] = 0; } $source_counts[ $src ]++; } arsort( $source_counts ); // Top landing pages. $landing_counts = array(); foreach ( $entries as $entry ) { if ( ! empty( $entry->landing_page ) ) { $path = wp_parse_url( $entry->landing_page, PHP_URL_PATH ); if ( ! $path ) { $path = '/'; } if ( ! isset( $landing_counts[ $path ] ) ) { $landing_counts[ $path ] = 0; } $landing_counts[ $path ]++; } } arsort( $landing_counts ); $top_landings = array_slice( $landing_counts, 0, 5, true ); // Conversion stats for this period. $conversions = 0; $sent_to_google = 0; foreach ( $entries as $entry ) { if ( ! empty( $entry->is_conversion ) ) { $conversions++; } if ( ! empty( $entry->conversion_sent ) ) { $sent_to_google++; } } $conversion_rate = $total > 0 ? round( ( $conversions / $total ) * 100, 1 ) : 0; // Trend calculation. if ( $prev_total > 0 ) { $change_pct = round( ( ( $total - $prev_total ) / $prev_total ) * 100 ); } elseif ( $total > 0 ) { $change_pct = 100; } else { $change_pct = 0; } // Build subject. $site_name = str_replace( array( "\r", "\n" ), '', get_bloginfo( 'name' ) ); $subject = sprintf( /* translators: 1: Site name, 2: Number of submissions */ __( '[%1$s] Monthly Lead Summary — %2$d submissions', 'forge-trueconversion' ), $site_name, $total ); // Build email body. $conversion_stats = array( 'conversions' => $conversions, 'conversion_rate' => $conversion_rate, 'sent_to_google' => $sent_to_google, ); $body = self::build_summary_html( $days, $total, $prev_total, $change_pct, $source_counts, $top_landings, $conversion_stats ); // Send. $headers = array( 'Content-Type: text/html; charset=UTF-8' ); wp_mail( $to, $subject, $body, $headers ); } /** * Build the HTML body for the monthly summary email. * * @param int $days Number of days covered. * @param int $total Total entries this period. * @param int $prev_total Total entries previous period. * @param int $change_pct Percentage change. * @param array $source_counts Traffic source => count. * @param array $top_landings Landing page path => count. * @param array $conversion_stats Conversion statistics. * * @return string HTML email body. */ public static function build_summary_html( $days, $total, $prev_total, $change_pct, $source_counts, $top_landings, $conversion_stats ) { // Trend indicator. if ( $change_pct > 0 ) { $trend = '▲ +' . $change_pct . '%'; } elseif ( $change_pct < 0 ) { $trend = '▼ ' . $change_pct . '%'; } else { $trend = '— 0%'; } $date_range = sprintf( /* translators: 1: start date, 2: end date */ __( '%1$s to %2$s', 'forge-trueconversion' ), date_i18n( get_option( 'date_format' ), strtotime( "-{$days} days" ) ), date_i18n( get_option( 'date_format' ) ) ); // Start HTML. $html = '

' . esc_html__( 'Monthly', 'forge-trueconversion' ) . ' ' . esc_html__( 'Lead Summary', 'forge-trueconversion' ) . '

' . esc_html( $date_range ) . '

' . esc_html__( 'Total Submissions', 'forge-trueconversion' ) . '

' . esc_html( $total ) . '

' . esc_html__( 'vs previous period', 'forge-trueconversion' ) . '

' . $trend . '

'; // Conversion stats section. if ( $conversion_stats['conversions'] > 0 || $conversion_stats['sent_to_google'] > 0 ) { $html .= '

' . esc_html__( 'Conversions', 'forge-trueconversion' ) . '

' . esc_html( $conversion_stats['conversions'] ) . ' ' . esc_html__( 'marked', 'forge-trueconversion' ) . ' ' . esc_html( $conversion_stats['conversion_rate'] ) . '% ' . esc_html__( 'rate', 'forge-trueconversion' ) . ' ' . esc_html( $conversion_stats['sent_to_google'] ) . ' ' . esc_html__( 'sent to Google', 'forge-trueconversion' ) . '
'; } // Traffic source breakdown. if ( ! empty( $source_counts ) ) { $html .= '

' . esc_html__( 'Traffic Sources', 'forge-trueconversion' ) . '

'; foreach ( $source_counts as $source => $count ) { $pct = $total > 0 ? round( ( $count / $total ) * 100 ) : 0; $html .= ' '; } $html .= '
' . esc_html( $source ) . ' ' . esc_html( $count ) . ' ' . esc_html( $pct ) . '%
'; } // Top landing pages. if ( ! empty( $top_landings ) ) { $html .= '

' . esc_html__( 'Top Landing Pages', 'forge-trueconversion' ) . '

'; foreach ( $top_landings as $path => $count ) { $html .= ' '; } $html .= '
' . esc_html( $path ) . ' ' . esc_html( $count ) . '
'; } // Footer. $html .= '

' . esc_html__( 'Sent by TrueConversion', 'forge-trueconversion' ) . ' — ' . esc_html__( 'View all entries', 'forge-trueconversion' ) . '

'; return $html; } } // Initialise cron hooks. FTCON_Summary_Emails::init();