Skip to content

Commit a0e5a95

Browse files
authored
Merge pull request #108 from SparkPost/issue97
Attachments in templates
2 parents 77ca17a + 15d558b commit a0e5a95

File tree

8 files changed

+471
-56
lines changed

8 files changed

+471
-56
lines changed

admin.widget.class.php

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ public function set_html_content_type()
4848
return 'text/html';
4949
}
5050

51-
private function send_email($recipient)
51+
private function send_email($recipient, $attachments = array())
5252
{
5353
add_filter('wp_mail_content_type', array($this, 'set_html_content_type'));
5454
$headers = array();
55-
$attachments= array(__DIR__ . '/sample.txt');
5655
$result = wp_mail($recipient,
5756
'SparkPost email test',
5857
'<h3>Hurray!!</h3><p>You\'ve got mail! <br/><br> Regards,<br/><a href="https://www.sparkpost.com">SparkPost</a> WordPress plugin</p>',
@@ -63,7 +62,7 @@ private function send_email($recipient)
6362
return $result;
6463
}
6564

66-
public function test_email_sending($recipient, $debug = false)
65+
public function test_email_sending($recipient, $debug = false, $include_attachment = false)
6766
{
6867
if (empty($recipient)) {
6968
return $this->render_message('Please enter a valid email address in the recipient field below.');
@@ -73,14 +72,21 @@ public function test_email_sending($recipient, $debug = false)
7372
return $this->render_message('Recipient is not a valid email address.');
7473
}
7574

75+
76+
if($include_attachment) {
77+
$attachments = array(__DIR__ . '/sample.txt');
78+
} else {
79+
$attachments = array();
80+
}
81+
7682
if ($debug) {
7783
add_action('phpmailer_init', array($this, 'phpmailer_enable_debugging'));
7884
echo '<div class="notice is-dismissible">';
7985
echo '<h4>Debug Messages</h4>';
80-
$result = $this->send_email($recipient);
86+
$result = $this->send_email($recipient, $attachments);
8187
echo '</div>';
8288
} else {
83-
$result = $this->send_email($recipient);
89+
$result = $this->send_email($recipient, $attachments);
8490
}
8591

8692
if ($result) {
@@ -111,7 +117,7 @@ public function wpsp_admin_page()
111117
<h3>Test Email</h3>
112118
<?php
113119
if (isset($_POST['sp_test_email'])) {
114-
$this->test_email_sending($_POST['to_email'], !empty($_POST['enable_debugging']));
120+
$this->test_email_sending($_POST['to_email'], !empty($_POST['enable_debugging']), !empty($_POST['include_attachment']));
115121
}
116122
?>
117123

@@ -144,6 +150,7 @@ public function admin_page_init()
144150

145151
add_settings_section('test_email', '', null, 'sp-test-email');
146152
add_settings_field('to_email', 'Recipient*', array($this, 'render_to_email_field'), 'sp-test-email', 'test_email');
153+
add_settings_field('include_attachment', '', array($this, 'render_include_attachment_field'), 'sp-test-email', 'test_email');
147154
add_settings_field('debug_messages', 'Debug', array($this, 'render_enable_debugging_field'), 'sp-test-email', 'test_email');
148155
}
149156

@@ -233,7 +240,8 @@ public function render_password_field()
233240

234241
printf(
235242
'<input type="text" id="password" name="sp_settings[password]" class="regular-text" value="%s" /><br/>
236-
<small><ul><li>For SMTP, set up an API key with the <strong>Send via SMTP</strong> permission</li> <li>For HTTP API, set up an API Key with the <strong>Transmissions: Read/Write</strong> permission</li><a href="https://support.sparkpost.com/customer/portal/articles/1933377-create-api-keys" target="_blank">Need help creating a SparkPost API key?</a></small>',
243+
<small><ul><li>For SMTP, set up an API key with the <strong>Send via SMTP</strong> permission</li>
244+
<li>For HTTP API, set up an API Key with the <strong>Transmissions: Read/Write, Templates: Read/Write</strong> permissions</li><a href="https://support.sparkpost.com/customer/portal/articles/1933377-create-api-keys" target="_blank">Need help creating a SparkPost API key?</a></small>',
237245
isset($api_key) ? $api_key : ''
238246
);
239247
}
@@ -247,7 +255,6 @@ public function render_template_field()
247255
<ul>
248256
<li>- Please see <a href="https://support.sparkpost.com/customer/portal/articles/2409547-using-templates-with-the-sparkpost-wordpress-plugin" target="_blank">this article</a> for detailed information about using templates with this plugin.</li>
249257
<li>- Templates can only be used with the HTTP API.</li>
250-
<li>- <a href="https://github.com/SparkPost/wordpress-sparkpost/blob/master/docs/templates-attachments.md" target="_blank">Does not work with attachment.</a>
251258
<li>- Leave this field blank to disable use of a template. You can still specify it by <a href="https://github.com/SparkPost/wordpress-sparkpost/blob/master/docs/hooks.md" target="_blank">using hooks</a>.</li>
252259
</ul>
253260
</small>
@@ -256,7 +263,7 @@ public function render_template_field()
256263

257264
public function render_from_email_field()
258265
{
259-
$hint = 'Important: Domain must match with one of your verified sending domains.';
266+
$hint = '<strong>Important:</strong> Domain must match with one of your verified sending domains.';
260267
if(empty($this->settings['from_email'])){
261268
$hostname = parse_url(get_bloginfo('url'), PHP_URL_HOST);
262269
$hint .= sprintf(' When left blank, <strong>%s</strong> will be used as email domain', $hostname);
@@ -309,11 +316,17 @@ public function render_enable_debugging_field()
309316
echo '<label><input type="checkbox" id="enable_debugging" name="enable_debugging" value="1" checked />Show email debugging messages</label>';
310317
}
311318

312-
public function render_transactional_field()
313-
{
314-
printf('<label><input type="checkbox" id="transactional" name="sp_settings[transactional]" value="1" %s />Mark emails as transactional</label>
315-
<br/><small>Upon checked, by default, it\'ll set mark all emails as transactional. It should be set false (using hooks) for non-transactional emails.</small>',
316-
$this->settings['transactional'] ? 'checked' : '');
319+
public function render_transactional_field()
320+
{
321+
printf('<label><input type="checkbox" id="transactional" name="sp_settings[transactional]" value="1" %s />Mark emails as transactional</label>
322+
<br/><small>Upon checked, by default, it\'ll set mark all emails as transactional. It should be set false (using hooks) for non-transactional emails.</small>',
323+
$this->settings['transactional'] ? 'checked' : '');
324+
325+
}
326+
317327

318-
}
328+
public function render_include_attachment_field()
329+
{
330+
echo '<label><input type="checkbox" id="include_attachment" name="include_attachment" value="1" />Include Attachment</label>';
331+
}
319332
}

docs/hooks.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ Hook names are prefixed with `wpsp_`.
2727
| wpsp_body_headers | Filter |
2828
| wpsp_smtp_msys_api | Filter |
2929
| wpsp_transactional | Filter | Set whether an email is transactional or not.
30+
| wpsp_substitution_data | Filter | Modify substitution_data object

mailer.http.class.php

Lines changed: 109 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
if (!defined('ABSPATH')) exit();
66

77
require_once ABSPATH . WPINC . '/class-phpmailer.php';
8+
require_once WPSP_PLUGIN_DIR . '/templates.class.php';
89

910
class SparkPostHTTPMailer extends \PHPMailer
1011
{
@@ -18,6 +19,7 @@ class SparkPostHTTPMailer extends \PHPMailer
1819
function __construct($exceptions = false)
1920
{
2021
$this->settings = SparkPost::get_settings();
22+
$this->template = new SparkPostTemplates($this);
2123

2224
parent::__construct($exceptions);
2325
do_action('wpsp_init_mailer', $this);
@@ -38,34 +40,55 @@ protected function mailSend($header, $body)
3840

3941
function sparkpost_send()
4042
{
41-
$this->edebug('Preparing request data');
43+
$this->debug('Preparing request data');
44+
45+
$request_body = $this->get_request_body();
46+
47+
if(!$request_body) {
48+
$this->error('Failed to prepare transmission request body');
49+
return false;
50+
}
4251

4352
$data = array(
4453
'method' => 'POST',
4554
'timeout' => 15,
4655
'headers' => $this->get_request_headers(),
47-
'body' => json_encode($this->get_request_body())
56+
'body' => json_encode($request_body)
4857
);
4958

50-
$http = apply_filters('wpsp_get_http_lib', _wp_http_get_object());
51-
52-
$this->edebug(sprintf('Request headers: %s', print_r($this->get_request_headers(true), true)));
53-
$this->edebug(sprintf('Request body: %s', $data['body']));
54-
$this->edebug(sprintf('Making HTTP POST request to %s', $this->endpoint));
55-
do_action('wpsp_before_send', $this->endpoint, $data);
56-
$result = $http->request($this->endpoint, $data);
57-
do_action('wpsp_after_send', $result);
58-
$this->edebug('Response received');
59+
$result = $this->request($this->endpoint, $data);
5960

6061
$result = apply_filters('wpsp_handle_response', $result);
62+
$this->check_permission_error($result, 'Transmissions: Read/Write');
6163
if(is_bool($result)) { // it means, response been already processed by the hooked filter. so just return the value.
62-
$this->edebug('Skipping response processing');
64+
$this->debug('Skipping response processing');
6365
return $result;
6466
} else {
6567
return $this->handle_response($result);
6668
}
6769
}
6870

71+
/**
72+
* Prepare substitution data to be used in template
73+
*/
74+
protected function get_template_substitutes($sender, $replyTo){
75+
$substitution_data = array();
76+
$substitution_data['content'] = $this->Body;
77+
$substitution_data['subject'] = $this->Subject;
78+
$substitution_data['from_name'] = $sender['name'];
79+
$substitution_data['from'] = $sender['email'];
80+
if ($replyTo) {
81+
$substitution_data['reply_to'] = $replyTo;
82+
}
83+
$localpart = explode('@', $sender['email']);
84+
85+
if (!empty($localpart)) {
86+
$substitution_data['from_localpart'] = $localpart[0];
87+
}
88+
89+
return apply_filters('wpsp_substitution_data', $substitution_data);
90+
}
91+
6992
/**
7093
* Build the request body to be sent to the SparkPost API.
7194
*/
@@ -88,23 +111,39 @@ protected function get_request_body()
88111

89112
$template_id = apply_filters('wpsp_template_id', $this->settings['template']);
90113

114+
$attachments = $this->get_attachments();
115+
91116
// pass through either stored template or inline content
92117
if (!empty($template_id)) {
93-
// stored template
94-
$body['content']['template_id'] = $template_id;
118+
// stored template
119+
$substitution_data = $this->get_template_substitutes($sender, $replyTo);
120+
if(sizeof($attachments) > 0){ //get template preview data and then send it as inline
121+
$preview_contents = $this->template->preview($template_id, $substitution_data);
122+
if($preview_contents === false) {
123+
return false;
124+
}
125+
$body['content'] = array(
126+
'from' => (array) $preview_contents->from,
127+
'subject' => (string) $preview_contents->subject,
128+
'headers' => (array) $this->get_headers()
129+
);
95130

96-
// supply substitution data so users can add variables to templates
97-
$body['substitution_data']['content'] = $this->Body;
98-
$body['substitution_data']['subject'] = $this->Subject;
99-
$body['substitution_data']['from_name'] = $sender['name'];
100-
$body['substitution_data']['from'] = $sender['email'];
101-
if ($replyTo) {
102-
$body['substitution_data']['reply_to'] = $replyTo;
131+
if(property_exists($preview_contents, 'text')) {
132+
$body['content']['text'] = $preview_contents->text;
103133
}
104-
$localpart = explode('@', $sender['email']);
105-
if (!empty($localpart)) {
106-
$body['substitution_data']['from_localpart'] = $localpart[0];
134+
135+
if(property_exists($preview_contents, 'html')){
136+
$body['content']['html'] = $preview_contents->html;
137+
}
138+
139+
if(property_exists($preview_contents, 'reply_to')) {
140+
$body['content']['reply_to'] = $preview_contents->reply_to;
107141
}
142+
143+
} else { // simply subsititute template tags
144+
$body['content']['template_id'] = $template_id;
145+
$body['substitution_data'] = $substitution_data;
146+
}
108147
} else {
109148
// inline content
110149
$body['content'] = array(
@@ -131,8 +170,7 @@ protected function get_request_body()
131170
}
132171
}
133172

134-
$attachments = $this->get_attachments();
135-
if (count($attachments)) {
173+
if (sizeof($attachments)) {
136174
$body['content']['attachments'] = $attachments;
137175
}
138176

@@ -191,41 +229,41 @@ public function isMail()
191229
protected function handle_response($response)
192230
{
193231
if (is_wp_error($response)) {
194-
$this->edebug('Request completed with error');
195-
$this->setError($response->get_error_messages()); //WP_Error implements this method
196-
$this->edebug($response->get_error_messages());
232+
$this->debug('Request completed with error');
233+
$this->error($response->get_error_messages()); //WP_Error implements this method
234+
$this->debug($response->get_error_messages());
197235
return false;
198236
}
199237

200-
$this->edebug('Response headers: ' . print_r($response['headers'], true));
201-
$this->edebug('Response body: ' . print_r($response['body'], true));
238+
$this->debug('Response headers: ' . print_r($response['headers'], true));
239+
$this->debug('Response body: ' . print_r($response['body'], true));
202240

203241
$body = json_decode($response['body']);
204242
do_action('wpsp_response_body', $body);
205243

206244
if (property_exists($body, 'errors')) {
207-
$this->edebug('Error in transmission');
208-
$this->setError($body->errors);
245+
$this->debug('Error in transmission');
246+
$this->error($body->errors);
209247
return false;
210248
}
211249

212250
if (property_exists($body, 'results')) {
213251
$data = $body->results;
214252
} else {
215-
$this->edebug('API response is unknown');
216-
$this->setError('Unknown response');
253+
$this->debug('API response is unknown');
254+
$this->error('Unknown response');
217255
return false;
218256
}
219257

220258
if ($data->total_rejected_recipients > 0) {
221-
$this->edebug(sprintf('Sending to %d recipient(s) failed', $data->total_rejected_recipients));
222-
$this->setError($data);
259+
$this->debug(sprintf('Sending to %d recipient(s) failed', $data->total_rejected_recipients));
260+
$this->error($data);
223261
return false;
224262
}
225263

226264
if ($data->total_accepted_recipients > 0) {
227-
$this->edebug(sprintf('Successfully sent to %d recipient(s)', $data->total_accepted_recipients));
228-
$this->edebug(sprintf('Transmission ID is %s', $data->id));
265+
$this->debug(sprintf('Successfully sent to %d recipient(s)', $data->total_accepted_recipients));
266+
$this->debug(sprintf('Transmission ID is %s', $data->id));
229267
return true;
230268
}
231269
return false;
@@ -259,7 +297,7 @@ protected function get_recipients()
259297
return apply_filters('wpsp_recipients', $recipients);
260298
}
261299

262-
protected function get_request_headers($hide_api_key = false)
300+
public function get_request_headers($hide_api_key = false)
263301
{
264302
$api_key = apply_filters('wpsp_api_key', $this->settings['password']);
265303
if ($hide_api_key) {
@@ -417,4 +455,35 @@ protected function get_headers()
417455

418456
return apply_filters('wpsp_body_headers', $formatted_headers);
419457
}
458+
459+
function check_permission_error($response, $permission) {
460+
$response = (array) $response;
461+
if(!empty($response['response']) && $response['response']['code'] === 403) {
462+
$this->debug("API Key might not have {$permission} permission. Actual Error: " . print_r($response['response'], true));
463+
$this->error("API Key might not have {$permission} permission");
464+
return true;
465+
}
466+
return false;
467+
}
468+
469+
public function debug($msg) {
470+
$this->edebug($msg);
471+
}
472+
473+
public function error($msg) {
474+
$this->setError($msg);
475+
}
476+
477+
public function request($endpoint, $data) {
478+
$http = apply_filters('wpsp_get_http_lib', _wp_http_get_object());
479+
480+
$this->debug(sprintf('Request headers: %s', print_r($this->get_request_headers(true), true)));
481+
$this->debug(sprintf('Request body: %s', $data['body']));
482+
$this->debug(sprintf('Making HTTP POST request to %s', $endpoint));
483+
do_action('wpsp_before_send', $this->endpoint, $data);
484+
$result = $http->request($endpoint, $data);
485+
do_action('wpsp_after_send', $result);
486+
$this->debug('Response received');
487+
return $result;
488+
}
420489
}

0 commit comments

Comments
 (0)