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 0.1.0
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-pkg' );
67        $this->description    = __( 'Email sent, with the CSV attached, when a report export is ready.', 'jetpack-premium-analytics-pkg' );
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        parent::__construct();
73
74        if ( null !== $logger ) {
75            $this->logger = $logger;
76        }
77
78        $this->recipient = $this->get_option( 'recipient', get_option( 'admin_email' ) );
79    }
80
81    /**
82     * Register the email.
83     *
84     * @return void
85     */
86    public function register(): void {
87        // Intentionally not hooked into woocommerce_email_classes. This is a transactional export
88        // email sent directly via send_export_email(), not an admin-configurable WooCommerce email,
89        // so it should not appear as a settings row under WooCommerce > Settings > Emails.
90    }
91
92    /**
93     * Get email subject.
94     *
95     * @return string
96     */
97    public function get_default_subject(): string {
98        return __( 'Your export is ready!', 'jetpack-premium-analytics-pkg' );
99    }
100
101    /**
102     * Get email heading.
103     *
104     * @return string
105     */
106    public function get_default_heading(): string {
107        return __( 'Your export is ready!', 'jetpack-premium-analytics-pkg' );
108    }
109
110    /**
111     * Get email subject with report name.
112     *
113     * @return string
114     */
115    public function get_subject(): string {
116        if ( ! empty( $this->report_label ) ) {
117            return sprintf(
118                /* translators: %s: Report name */
119                __( 'Your %s export is ready!', 'jetpack-premium-analytics-pkg' ),
120                $this->report_label
121            );
122        }
123        return $this->get_default_subject();
124    }
125
126    /**
127     * Get email heading with report name.
128     *
129     * @return string
130     */
131    public function get_heading(): string {
132        if ( ! empty( $this->report_label ) ) {
133            return sprintf(
134                /* translators: %s: Report name */
135                __( 'Your %s export is ready!', 'jetpack-premium-analytics-pkg' ),
136                $this->report_label
137            );
138        }
139        return $this->get_default_heading();
140    }
141
142    /**
143     * Send export ready email.
144     *
145     * @param string $recipient    Recipient email address.
146     * @param string $report_label Report label.
147     * @param array  $params       Report parameters.
148     * @param string $file_path    Path to CSV file.
149     * @return bool True if email sent successfully.
150     */
151    public function send_export_email(
152        string $recipient,
153        string $report_label,
154        array $params,
155        string $file_path
156    ): bool {
157        $this->recipient = $recipient;
158
159        // Read back by get_content_html() / get_content_plain() when rendering the template.
160        $this->report_label = $report_label;
161        $this->params       = $params;
162
163        // The CSV is delivered as an attachment only (no public URL). If it is missing,
164        // unreadable, or too large to attach, fail rather than send a linkless dead-end email.
165        $file_size = is_readable( $file_path ) ? filesize( $file_path ) : false;
166        if ( false === $file_size || $file_size >= self::MAX_ATTACHMENT_SIZE ) {
167            if ( null !== $this->logger ) {
168                $this->logger->log_error(
169                    sprintf( 'Export file missing or too large to attach: %s', $file_path ),
170                    __METHOD__
171                );
172            }
173            return false;
174        }
175
176        $attachments = array( $file_path );
177
178        $sent = $this->send(
179            $this->get_recipient(),
180            $this->get_subject(),
181            $this->get_content(),
182            $this->get_headers(),
183            $attachments
184        );
185
186        if ( null === $this->logger ) {
187            return $sent;
188        }
189
190        if ( $sent ) {
191            $this->logger->log_message(
192                sprintf( 'Export email sent to: %s', $recipient ),
193                __METHOD__
194            );
195        } else {
196            $this->logger->log_error(
197                sprintf( 'Failed to send export email to: %s', $recipient ),
198                __METHOD__
199            );
200        }
201
202        return $sent;
203    }
204
205    /**
206     * Get content html.
207     *
208     * @return string
209     */
210    public function get_content_html(): string {
211        return wc_get_template_html(
212            $this->template_html,
213            array(
214                'email'         => $this,
215                'report_label'  => $this->report_label ?? '',
216                'params'        => $this->params ?? array(),
217                'email_heading' => $this->get_heading(),
218                'sent_to_admin' => false,
219                'is_comparison' => $this->is_comparison_request( $this->params ?? array() ),
220            ),
221            '',
222            $this->template_base
223        );
224    }
225
226    /**
227     * Get content plain.
228     *
229     * @return string
230     */
231    public function get_content_plain(): string {
232        return wc_get_template_html(
233            $this->template_plain,
234            array(
235                'email'         => $this,
236                'report_label'  => $this->report_label ?? '',
237                'params'        => $this->params ?? array(),
238                'email_heading' => $this->get_heading(),
239                'sent_to_admin' => false,
240                'is_comparison' => $this->is_comparison_request( $this->params ?? array() ),
241            ),
242            '',
243            $this->template_base
244        );
245    }
246}