Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
3.45% covered (danger)
3.45%
2 / 58
16.67% covered (danger)
16.67%
1 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
API
3.45% covered (danger)
3.45%
2 / 58
16.67% covered (danger)
16.67%
1 / 6
457.64
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get_integrations
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 handle_bounces
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
42
 handle_complaints
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
30
 handle_unsubscribe_emails
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
30
 set_log_level
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2
3namespace BrianHenryIE\AWS_SES_Bounce_Handler\API;
4
5use BrianHenryIE\AWS_SES_Bounce_Handler\API\Integrations\MailPoet;
6use BrianHenryIE\AWS_SES_Bounce_Handler\API\Integrations\Newsletter;
7use BrianHenryIE\AWS_SES_Bounce_Handler\API\Integrations\WooCommerce;
8use BrianHenryIE\AWS_SES_Bounce_Handler\API\Integrations\WordPress;
9use BrianHenryIE\AWS_SES_Bounce_Handler\API_Interface;
10use BrianHenryIE\AWS_SES_Bounce_Handler\Settings_Interface;
11use Psr\Log\LoggerAwareTrait;
12use Psr\Log\LoggerInterface;
13use Psr\Log\LogLevel;
14use stdClass;
15
16class API implements API_Interface {
17
18    use LoggerAwareTrait;
19
20    /**
21     * The settings object contains the AWS ARNs to listen to, as configured by the user.
22     *
23     * @var Settings_Interface
24     */
25    protected $settings;
26
27    /**
28     * Initialize the class and set its properties.
29     *
30     * @param Settings_Interface $settings The settings containing the ARNs to listen for.
31     * @param LoggerInterface    $logger PSR logger.
32     *
33     * @since    1.0.0
34     */
35    public function __construct( Settings_Interface $settings, LoggerInterface $logger ) {
36
37        $this->setLogger( $logger );
38        $this->settings = $settings;
39    }
40
41
42    /**
43     * Find and return all integrations.
44     *
45     * @return SES_Bounce_Handler_Integration_Interface[]
46     */
47    public function get_integrations(): array {
48
49        $built_in_integrations                = array();
50        $built_in_integrations['WordPress']   = new WordPress( $this->logger );
51        $built_in_integrations['WooCommerce'] = new WooCommerce( $this->logger );
52        $built_in_integrations['Newsletter']  = new Newsletter( $this->logger );
53        $built_in_integrations['MailPoet']    = new MailPoet( $this->logger );
54
55        $integrations = apply_filters( 'bh_wp_aws_ses_bounce_handler_integrations', $built_in_integrations );
56
57        // Clean the data.
58        $integrations = array_filter(
59            $integrations,
60            function( $integration ) {
61                return $integration instanceof SES_Bounce_Handler_Integration_Interface;
62            }
63        );
64
65        return $integrations;
66    }
67
68
69    /**
70     * When a bounce notification is received from SES fire the action for integrations and other plugins to hook into.
71     *
72     * @hooked bh_aws_ses_notification
73     *
74     * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-examples.html
75     *
76     * @param string   $notification_topic_arn  The ARN of the received notification.
77     * @param array    $headers                 HTTP headers received from AWS SNS.
78     * @param stdClass $body                    HTTP body received from AWS SNS.
79     * @param stdClass $message                 The (potential) bounce report object from AWS SES.
80     *
81     * phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
82     */
83    public function handle_bounces( string $notification_topic_arn, array $headers, stdClass $body, stdClass $message ): void {
84        if ( 'Bounce' !== $message->notificationType ) {
85            return;
86        }
87
88        if ( 'Permanent' === $message->bounce->bounceType ) {
89
90            foreach ( $message->bounce->bouncedRecipients as $bounced_recipient ) {
91
92                $email_address = sanitize_email( $bounced_recipient->emailAddress );
93
94                foreach ( $this->get_integrations() as $integration ) {
95
96                    if ( $integration->is_enabled() ) {
97                        $integration->init();
98                        $integration->handle_ses_bounce( $email_address, $bounced_recipient, $message );
99                    }
100                }
101
102                /**
103                 * Action to allow other plugins to act on SES bounce notification.
104                 *
105                 * @param string $email_address     The email address that has bounced.
106                 * @param stdClass $bounced_recipient Parent object with emailAddress, status, action, diagnosticCode.
107                 * @param stdClass $message           Parent object of complete notification.
108                 *
109                 * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-examples.html
110                 */
111                do_action( 'handle_ses_bounce', $email_address, $bounced_recipient, $message );
112            }
113        }
114    }
115
116    /**
117     * When a complaint notification is received from SES fire the action for integrations and other plugins to hook into.
118     *
119     * @hooked filter bh_aws_sns_notification
120     *
121     * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-examples.html
122     *
123     * @param string   $notification_topic_arn  The ARN of the received notification.
124     * @param array    $headers                 HTTP headers received from AWS SNS.
125     * @param stdClass $body                    HTTP body received from AWS SNS.
126     * @param stdClass $message                 The (potential) complaint report object from AWS SES.
127     */
128    public function handle_complaints( string $notification_topic_arn, array $headers, stdClass $body, stdClass $message ): void {
129
130        if ( 'Complaint' !== $message->notificationType ) {
131            return;
132        }
133
134        foreach ( $message->complaint->complainedRecipients as $complained_recipient ) {
135
136            $email_address = sanitize_email( $complained_recipient->emailAddress );
137
138            foreach ( $this->get_integrations() as $integration ) {
139
140                if ( $integration->is_enabled() ) {
141                    $integration->init();
142                    $integration->handle_ses_complaint( $email_address, $complained_recipient, $message );
143                }
144            }
145
146            /**
147             * Action to allow other plugins to act on SES complaint notifications.
148             *
149             * @param string $email_address     The email address that has complained.
150             * @param stdClass $bounced_recipient Parent object with emailAddress, status, action, diagnosticCode.
151             * @param stdClass $message           Parent object of complete notification.
152             *
153             * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-examples.html
154             */
155            do_action( 'handle_ses_complaint', $email_address, $complained_recipient, $message );
156        }
157    }
158
159
160    /**
161     * When a complaint notification is received from SES fire the action for integrations and other plugins to hook into.
162     *
163     * @hooked filter ea_aws_sns_notification
164     *
165     * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-examples.html
166     *
167     * @param string   $notification_topic_arn  The ARN of the received notification.
168     * @param array    $headers                 HTTP headers received from AWS SNS.
169     * @param stdClass $body                    HTTP body received from AWS SNS.
170     * @param stdClass $message                 The (potential) complaint report object from AWS SES.
171     */
172    public function handle_unsubscribe_emails( string $notification_topic_arn, array $headers, stdClass $body, stdClass $message ): void {
173
174        if ( 'Received' !== $message->notificationType ) {
175            return;
176        }
177
178        $email = $message->mail;
179
180        $email_address = sanitize_email( $email->source );
181
182        foreach ( $this->get_integrations() as $integration ) {
183
184            if ( $integration->is_enabled() ) {
185                $integration->init();
186                if ( method_exists( $integration, 'handle_unsubscribe_email' ) ) {
187                    $integration->handle_unsubscribe_email( $email_address, $email, $message );
188                }
189            }
190        }
191
192        /**
193         * Action to allow other plugins to act on SES complaint notifications.
194         *
195         * @param string $email_address     The email address that has complained.
196         * @param stdClass $email Parent object with emailAddress, status, action, diagnosticCode.
197         * @param stdClass $message           Parent object of complete notification.
198         *
199         * @see https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-examples.html
200         */
201        do_action( 'handle_unsubscribe_email', $email_address, $email, $message );
202
203    }
204
205    /**
206     * Set the log level for the plugin.
207     *
208     * @param string $level The PSR log level to set, or "none".
209     *
210     * @return array{success:bool, message:string}
211     */
212    public function set_log_level( string $level ): array {
213
214        $result = array();
215
216        $allowed_log_levels = array(
217            'none',
218            LogLevel::ERROR,
219            LogLevel::WARNING,
220            LogLevel::NOTICE,
221            LogLevel::INFO,
222            LogLevel::DEBUG,
223        );
224
225        if ( ! in_array( $level, $allowed_log_levels, true ) ) {
226            $result['success'] = false;
227            /* translators: %s is the supplied new log level */
228            $result['message'] = sprintf( __( '`%s` is not a valid log level', 'bh-wp-aws-ses-bounce-handler' ), $level );
229            return $result;
230        }
231
232        $current_log_level = $this->settings->get_log_level();
233
234        if ( $level === $current_log_level ) {
235            $result['success'] = true;
236            /* translators: %s existing log level */
237            $result['message'] = sprintf( __( 'Log level already set to `%s`.', 'bh-wp-aws-ses-bounce-handler' ), $level );
238            return $result;
239        }
240
241        $success = update_option( Settings_Interface::LOG_LEVEL_OPTION_NAME, $level );
242
243        $result['success'] = $success;
244
245        if ( $success ) {
246            /* translators: %1s was the previous log level, %2s is the new log level */
247            $result['message'] = sprintf( __( 'Log level changed from `%1$s` to `%2$s`', 'bh-wp-aws-ses-bounce-handler' ), $current_log_level, $level );
248        } else {
249            /* translators: %1s is the desired new log level, %2s was the previous log level */
250            $result['message'] = sprintf( __( 'Error setting log level to `%1$s`. Log level is unchanged at `%2$s`.', 'bh-wp-aws-ses-bounce-handler' ), $level, $current_log_level );
251        }
252
253        return $result;
254    }
255}