Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
36.05% covered (danger)
36.05%
31 / 86
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Csv_Export_Email
37.80% covered (danger)
37.80%
31 / 82
0.00% covered (danger)
0.00%
0 / 9
95.95
0.00% covered (danger)
0.00%
0 / 1
 __construct
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
2.00
 register
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_default_subject
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_default_heading
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get_subject
83.33% covered (warning)
83.33%
5 / 6
0.00% covered (danger)
0.00%
0 / 1
2.02
 get_heading
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 send_export_email
54.84% covered (warning)
54.84%
17 / 31
0.00% covered (danger)
0.00%
0 / 1
11.51
 get_content_html
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
 get_content_plain
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * CSV Export Email
4 *
5 * Handles sending email notifications for CSV exports.
6 *
7 * @package Automattic\Jetpack\PremiumAnalytics\Reports\Export
8 */
9
10declare( strict_types=1 );
11
12namespace Automattic\Jetpack\PremiumAnalytics\Reports\Export;
13
14defined( 'ABSPATH' ) || exit;
15
16use Automattic\Jetpack\PremiumAnalytics\Reports\Export\Logging\Logger_Interface;
17use Automattic\Jetpack\PremiumAnalytics\Reports\Export\Support\Logger_Trait;
18use Automattic\Jetpack\PremiumAnalytics\Reports\Export\Support\Utilities;
19
20/**
21 * Include WC_Email dependencies if not already loaded.
22 */
23if ( ! class_exists( 'WC_Email', false ) && function_exists( 'WC' ) ) {
24    // @phan-suppress-next-line PhanUndeclaredConstant -- WC_PLUGIN_FILE is defined by WooCommerce, guarded by function_exists( 'WC' ).
25    include_once dirname( WC_PLUGIN_FILE ) . '/includes/class-wc-emails.php';
26    // @phan-suppress-next-line PhanUndeclaredConstant -- WC_PLUGIN_FILE is defined by WooCommerce, guarded by function_exists( 'WC' ).
27    include_once dirname( WC_PLUGIN_FILE ) . '/includes/emails/class-wc-email.php';
28}
29
30/**
31 * CSV Export Email class.
32 *
33 * @since $$next-version$$
34 */
35class Csv_Export_Email extends \WC_Email implements Registrable_Interface {
36
37    use Logger_Trait;
38    use Utilities;
39
40    /**
41     * Maximum file size for email attachments in bytes (10MB).
42     */
43    const MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
44
45    /**
46     * Report label for the export.
47     *
48     * @var string
49     */
50    private $report_label = '';
51
52    /**
53     * Export parameters.
54     *
55     * @var array
56     */
57    private $params = array();
58
59    /**
60     * Constructor.
61     *
62     * @param Logger_Interface|null $logger The logger instance.
63     */
64    public function __construct( ?Logger_Interface $logger = null ) {
65        $this->id             = 'csv_export_ready';
66        $this->title          = __( 'CSV Export Ready', 'jetpack-premium-analytics' );
67        $this->description    = __( 'Email sent, with the CSV attached, when a report export is ready.', 'jetpack-premium-analytics' );
68        $this->template_html  = 'csv-export-email.php';
69        $this->template_plain = 'csv-export-email-plain.php';
70        $this->template_base  = __DIR__ . '/templates/';
71
72        // Call parent constructor.
73        parent::__construct();
74
75        // Set logger.
76        if ( null !== $logger ) {
77            $this->logger = $logger;
78        }
79
80        // Other settings.
81        $this->recipient = $this->get_option( 'recipient', get_option( 'admin_email' ) );
82    }
83
84    /**
85     * Register the email.
86     *
87     * @return void
88     */
89    public function register(): void {
90        // Intentionally not hooked into woocommerce_email_classes. This is a transactional export
91        // email sent directly via send_export_email(), not an admin-configurable WooCommerce email,
92        // so it should not appear as a settings row under WooCommerce > Settings > Emails.
93    }
94
95    /**
96     * Get email subject.
97     *
98     * @return string
99     */
100    public function get_default_subject(): string {
101        return __( 'Your export is ready!', 'jetpack-premium-analytics' );
102    }
103
104    /**
105     * Get email heading.
106     *
107     * @return string
108     */
109    public function get_default_heading(): string {
110        return __( 'Your export is ready!', 'jetpack-premium-analytics' );
111    }
112
113    /**
114     * Get email subject with report name.
115     *
116     * @return string
117     */
118    public function get_subject(): string {
119        if ( ! empty( $this->report_label ) ) {
120            return sprintf(
121                /* translators: %s: Report name */
122                __( 'Your %s export is ready!', 'jetpack-premium-analytics' ),
123                $this->report_label
124            );
125        }
126        return $this->get_default_subject();
127    }
128
129    /**
130     * Get email heading with report name.
131     *
132     * @return string
133     */
134    public function get_heading(): string {
135        if ( ! empty( $this->report_label ) ) {
136            return sprintf(
137                /* translators: %s: Report name */
138                __( 'Your %s export is ready!', 'jetpack-premium-analytics' ),
139                $this->report_label
140            );
141        }
142        return $this->get_default_heading();
143    }
144
145    /**
146     * Send export ready email.
147     *
148     * @param string $recipient    Recipient email address.
149     * @param string $report_label Report label.
150     * @param array  $params       Report parameters.
151     * @param string $file_path    Path to CSV file.
152     * @return bool True if email sent successfully.
153     */
154    public function send_export_email(
155        string $recipient,
156        string $report_label,
157        array $params,
158        string $file_path
159    ): bool {
160        // Set recipient.
161        $this->recipient = $recipient;
162
163        // Store data for template.
164        $this->report_label = $report_label;
165        $this->params       = $params;
166
167        // The CSV is delivered as an attachment only (no public URL). If it is missing,
168        // unreadable, or too large to attach, fail rather than send a linkless dead-end email.
169        $file_size = is_readable( $file_path ) ? filesize( $file_path ) : false;
170        if ( false === $file_size || $file_size >= self::MAX_ATTACHMENT_SIZE ) {
171            if ( null !== $this->logger ) {
172                $this->logger->log_error(
173                    sprintf( 'Export file missing or too large to attach: %s', $file_path ),
174                    __METHOD__
175                );
176            }
177            return false;
178        }
179
180        $attachments = array( $file_path );
181
182        // Send email.
183        $sent = $this->send(
184            $this->get_recipient(),
185            $this->get_subject(),
186            $this->get_content(),
187            $this->get_headers(),
188            $attachments
189        );
190
191        // Simply return if no logger available.
192        if ( null === $this->logger ) {
193            return $sent;
194        }
195
196        // Log the result.
197        if ( $sent ) {
198            $this->logger->log_message(
199                sprintf( 'Export email sent to: %s', $recipient ),
200                __METHOD__
201            );
202        } else {
203            $this->logger->log_error(
204                sprintf( 'Failed to send export email to: %s', $recipient ),
205                __METHOD__
206            );
207        }
208
209        return $sent;
210    }
211
212    /**
213     * Get content html.
214     *
215     * @return string
216     */
217    public function get_content_html(): string {
218        return wc_get_template_html(
219            $this->template_html,
220            array(
221                'email'         => $this,
222                'report_label'  => $this->report_label ?? '',
223                'params'        => $this->params ?? array(),
224                'email_heading' => $this->get_heading(),
225                'sent_to_admin' => false,
226                'is_comparison' => $this->is_comparison_request( $this->params ?? array() ),
227            ),
228            '',
229            $this->template_base
230        );
231    }
232
233    /**
234     * Get content plain.
235     *
236     * @return string
237     */
238    public function get_content_plain(): string {
239        return wc_get_template_html(
240            $this->template_plain,
241            array(
242                'email'         => $this,
243                'report_label'  => $this->report_label ?? '',
244                'params'        => $this->params ?? array(),
245                'email_heading' => $this->get_heading(),
246                'sent_to_admin' => false,
247                'is_comparison' => $this->is_comparison_request( $this->params ?? array() ),
248            ),
249            '',
250            $this->template_base
251        );
252    }
253}