Skip to content

Create a reusable Email class #11564

@benbowler

Description

@benbowler

Feature Description

Create a generalised Email class to abstract:

  • Combine template layout to HTML
  • Collect and apply to, subject, headers etc for mail sending
  • Send via wp_mail
  • Monitor and return errors using wp_mail_failed hook

This class should not be email report specific, allowing separate templates in future for other plugin emails. The namespace and structure should reflect this.


Do not alter or remove anything below. The following sections will be managed by moderators only.

Acceptance Criteria

  • A new, reusable class named Google\Site_Kit\Core\Util\Email is created to abstract the logic for sending emails.
  • The class includes a method to generate HTML email content by combining a template layout with provided data.
  • The class contains a method responsible for building and setting email properties, including the recipient address (to), subject line, and headers, to provide to wp_mail.
    • Email Header preserves the site’s configured ‘From’ email address; only the display name is overridden to Site Kit {site_url} (using the existing address returned by WordPress).
  • The class implements a method that utilizes the WordPress wp_mail function to send the email.
  • The class is capable of capturing and handling errors that occur during the email sending process by monitoring the wp_mail_failed hook.
    • Error monitoring using the wp_mail_failed hook must be explicitly added before the wp_mail call and removed immediately after the wp_mail call to ensure the error tracking is correctly scoped only to the current email send attempt.
  • If wp_mail returns false or an error is caught via the hook, the method should return or communicate the error state.

Implementation Brief

  • Add Google\Site_Kit\Core\Email_Reporting\Sections_Map which returns an array describing the layout sections, for example:
   return array(
        'is_my_site_helping_my_business_grow' => array(
            'title'                 => esc_html__( 'Is my site helping my business grow?', 'google-site-kit' ),
            'icon'                 => 'icon-name',
            'section_parts' => array(
                'total_conversion_events' => array(
                    'template' => 'metrics',
                    'data'     => $payload['total_conversion_events'],
                ),
                'products_added_to_cart' => array(
                    'template' => 'conversion-metrics',
                    'data'     => $payload['products_added_to_cart'],
                ),
             ),
        // … rest of the sections ...
    );
  • Icons will map to a PNG stored in the assets folder (later bucket) keyed via name via a helper added in Create email report base template #11549.
  • Map the sections based on the titles in Figma, and section parts based on their mapping in the report options classes added in Add report options builder classes for Analytics, AdSense and SC #11552
  • Section part templates should match one of the dynamic parts page-metrics, metrics or conversion-metrics mostly implemented in Create email report base template #11549
  • You can include get_sections method which returns the final array, which can be assembled from different methods each representing a section, which is finally merged into single array. Or it can just be returned as single array
  • Add Google\Site_Kit\Core\Email_Reporting\Email_Template_Renderer class
    • Add template rendering render( $data ) method
    • Get the config array from Google\Site_Kit\Core\Email_Reporting\Sections_Map class (you can extract this into separate method like get_sections_map) then render each section in order:
      * Iterate the sections map, along with the data array when including the template part file. The passed $data will have mapped data with keys named by each section part and data will hold Email_Report_Data_Section instance.
      * Skip including section parts not mapped in the data request array - so sections to which user has no access are not included for example check isset( $data[ $section_part_key ] )
    • Resolve the main template file from Google\Site_Kit\Core\Email_Reporting\templates. If missing, return empty string.
      • Wrap the include of template.php in an output buffer, and append the rendered HTML to a single string.
  • Create Google\Site_Kit\Core\Util\Email utility
    • Header builder (build_headers)
      • Fetch the filtered From email via wp_mail_from. Do not change the address.
      • Override the "from" header to Site Kit.
      • Merge caller-supplied headers and return the final header array.
    • Add Send method
      • send( $to, $subject, $content, $headers = array() )
      • Wrap the wp_mail call with a scoped listener:
        • Reset $this->last_error to null.
        • Define a closure $listener = function( WP_Error $error ) { $this->last_error = $error; };.
        • Call add_action( 'wp_mail_failed', $listener ); before invoking wp_mail().
        • After wp_mail() returns, immediately call remove_action( 'wp_mail_failed', $listener ); so the hook only applies to this send.
        • If wp_mail() returned false or the listener captured an error, return that WP_Error; otherwise return true.
        • Expose get_last_error() that returns the stored WP_Error or null in $this->last_error so callers can inspect the most recent failure message.

Test Coverage

  • Add tests for Email_Template_Renderer:
    • render() returns concatenated HTML when template.php and part files exist, and returns an empty string when the template key is missing.
    • Sections not present in $data are skipped (verify the markup omits them).
  • Add tests for Email class:
    • build_headers keeps the original From email (mock wp_mail_from) and updates only the display name to Site Kit {host}; additional headers merge correctly.
    • send() returns true when wp_mail is stubbed to succeed and no failure hook fires.
    • send() returns a WP_Error and stores it via get_last_error() when the wp_mail_failed hook is triggered or when wp_mail returns false.

QA Brief

Changelog entry

  • Add a class to send emails.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0High priorityPHPTeam SIssues for Squad 1Type: EnhancementImprovement of an existing feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions