diff options
author | yehudah <yehudah@b8457f37-d9ea-0310-8a92-e5e31aec5664> | 2017-11-17 13:03:43 +0000 |
---|---|---|
committer | yehudah <yehudah@b8457f37-d9ea-0310-8a92-e5e31aec5664> | 2017-11-17 13:03:43 +0000 |
commit | 3573c371f56ad0e4739cae230925833eb31b8158 (patch) | |
tree | 0a48cb82a796c6b7e2432741b03995cb2bae611e | |
parent | fa49f47e2f3db3115a9af66ebd404bb66d0c520b (diff) | |
download | Post-SMTP-3573c371f56ad0e4739cae230925833eb31b8158.zip |
= 1.7.8 - 2017-11-17
* = Menu Items grouping =
* Fixed: IP detection error in some web hosts
* Fixed: Link open in new page attribute = _blank
* Fixed: Replace deprecated PHP 7 functions.
* Updated: Validator TLD's list
* Added: Email log date and search filter.
* Added: Alert on sending error (Fallback to local mail)
* Added: Email body preview (not raw)
git-svn-id: https://plugins.svn.wordpress.org/post-smtp/trunk@1769444 b8457f37-d9ea-0310-8a92-e5e31aec5664
-rw-r--r-- | Postman/Postman-Email-Log/PostmanEmailLogController.php | 23 | ||||
-rw-r--r-- | Postman/Postman-Email-Log/PostmanEmailLogService.php | 236 | ||||
-rw-r--r-- | Postman/Postman-Email-Log/PostmanEmailLogView.php | 1 | ||||
-rw-r--r-- | Postman/PostmanPluginFeedback.php | 4 | ||||
-rw-r--r-- | Postman/PostmanUtils.php | 8 | ||||
-rw-r--r-- | Postman/PostmanViewController.php | 2 | ||||
-rw-r--r-- | postman-smtp.php | 2 | ||||
-rw-r--r-- | readme.txt | 10 |
8 files changed, 156 insertions, 130 deletions
diff --git a/Postman/Postman-Email-Log/PostmanEmailLogController.php b/Postman/Postman-Email-Log/PostmanEmailLogController.php index 1cdbee3..a7bd78a 100644 --- a/Postman/Postman-Email-Log/PostmanEmailLogController.php +++ b/Postman/Postman-Email-Log/PostmanEmailLogController.php @@ -20,7 +20,7 @@ class PostmanEmailLogController { add_action( 'admin_menu', array( $this, 'postmanAddMenuItem', - ) ); + ),20 ); } else { $this->logger->trace( 'not creating PostmanEmailLog admin menu item' ); } @@ -227,13 +227,20 @@ class PostmanEmailLogController { print '</table>'; print '<hr/>'; print '<pre>'; - print esc_html( $post->post_content ); + print $this->sanitize_message( $post->post_content ); print '</pre>'; print '</body></html>'; die(); } } + function sanitize_message( $message ) { + $allowed_tags = wp_kses_allowed_html( 'post' ); + $allowed_tags['style'] = array(); + + return wp_kses( $message, $allowed_tags ); + } + /** */ function view_transcript_log_item() { @@ -279,11 +286,13 @@ class PostmanEmailLogController { // only do this for administrators if ( PostmanUtils::isAdmin() ) { $this->logger->trace( 'created PostmanEmailLog admin menu item' ); - /* Translators where (%s) is the name of the plugin */ - $page = add_management_page( sprintf( __( '%s Email Log', Postman::TEXT_DOMAIN ), __( 'Postman SMTP', Postman::TEXT_DOMAIN ) ), _x( 'Email Log', 'The log of Emails that have been delivered', Postman::TEXT_DOMAIN ), 'read_private_posts', 'postman_email_log', array( - $this, - 'postman_render_email_page', - ) ); + /* + Translators where (%s) is the name of the plugin */ + $pageTitle = sprintf( __( '%s Email Log', Postman::TEXT_DOMAIN ), __( 'Postman SMTP', Postman::TEXT_DOMAIN ) ); + $pluginName = _x( 'Email Log', 'The log of Emails that have been delivered', Postman::TEXT_DOMAIN ); + + $page = add_submenu_page( PostmanViewController::POSTMAN_MENU_SLUG, $pageTitle, $pluginName, 'read_private_posts', 'postman_email_log', array( $this, 'postman_render_email_page' ) ); + // When the plugin options page is loaded, also load the stylesheet add_action( 'admin_print_styles-' . $page, array( $this, diff --git a/Postman/Postman-Email-Log/PostmanEmailLogService.php b/Postman/Postman-Email-Log/PostmanEmailLogService.php index b6dd98b..e5c4bfe 100644 --- a/Postman/Postman-Email-Log/PostmanEmailLogService.php +++ b/Postman/Postman-Email-Log/PostmanEmailLogService.php @@ -1,5 +1,5 @@ <?php -if (! class_exists ( 'PostmanEmailLog' )) { +if ( ! class_exists( 'PostmanEmailLog' ) ) { class PostmanEmailLog { public $sender; public $toRecipients; @@ -19,15 +19,15 @@ if (! class_exists ( 'PostmanEmailLog' )) { } } -if (! class_exists ( 'PostmanEmailLogService' )) { - +if ( ! class_exists( 'PostmanEmailLogService' ) ) { + /** * This class creates the Custom Post Type for Email Logs and handles writing these posts. * * @author jasonhendriks */ class PostmanEmailLogService { - + /* * Private content is published only for your eyes, or the eyes of only those with authorization * permission levels to see private content. Normal users and visitors will not be aware of @@ -36,170 +36,182 @@ if (! class_exists ( 'PostmanEmailLogService' )) { * the private content when you are logged into your WordPress blog. */ const POSTMAN_CUSTOM_POST_STATUS_PRIVATE = 'private'; - + // member variables private $logger; private $inst; - + /** * Constructor */ private function __construct() { - $this->logger = new PostmanLogger ( get_class ( $this ) ); + $this->logger = new PostmanLogger( get_class( $this ) ); } - + /** * singleton instance */ public static function getInstance() { static $inst = null; - if ($inst === null) { - $inst = new PostmanEmailLogService (); + if ( $inst === null ) { + $inst = new PostmanEmailLogService(); } return $inst; } - + /** * Logs successful email attempts * - * @param PostmanMessage $message - * @param unknown $transcript - * @param PostmanModuleTransport $transport + * @param PostmanMessage $message + * @param unknown $transcript + * @param PostmanModuleTransport $transport */ - public function writeSuccessLog(PostmanEmailLog $log, PostmanMessage $message, $transcript, PostmanModuleTransport $transport) { - if (PostmanOptions::getInstance ()->isMailLoggingEnabled ()) { + public function writeSuccessLog( PostmanEmailLog $log, PostmanMessage $message, $transcript, PostmanModuleTransport $transport ) { + if ( PostmanOptions::getInstance()->isMailLoggingEnabled() ) { $statusMessage = ''; $status = true; - $subject = $message->getSubject (); - if (empty ( $subject )) { - $statusMessage = sprintf ( '%s: %s', __ ( 'Warning', Postman::TEXT_DOMAIN ), __ ( 'An empty subject line can result in delivery failure.', Postman::TEXT_DOMAIN ) ); + $subject = $message->getSubject(); + if ( empty( $subject ) ) { + $statusMessage = sprintf( '%s: %s', __( 'Warning', Postman::TEXT_DOMAIN ), __( 'An empty subject line can result in delivery failure.', Postman::TEXT_DOMAIN ) ); $status = 'WARN'; } - $this->createLog ( $log, $message, $transcript, $statusMessage, $status, $transport ); - $this->writeToEmailLog ( $log ); + $this->createLog( $log, $message, $transcript, $statusMessage, $status, $transport ); + $this->writeToEmailLog( $log ); } } - + /** * Logs failed email attempts, requires more metadata so the email can be resent in the future * - * @param PostmanMessage $message - * @param unknown $transcript - * @param PostmanModuleTransport $transport - * @param unknown $statusMessage - * @param unknown $originalTo - * @param unknown $originalSubject - * @param unknown $originalMessage - * @param unknown $originalHeaders + * @param PostmanMessage $message + * @param unknown $transcript + * @param PostmanModuleTransport $transport + * @param unknown $statusMessage + * @param unknown $originalTo + * @param unknown $originalSubject + * @param unknown $originalMessage + * @param unknown $originalHeaders */ - public function writeFailureLog(PostmanEmailLog $log, PostmanMessage $message = null, $transcript, PostmanModuleTransport $transport, $statusMessage) { - if (PostmanOptions::getInstance ()->isMailLoggingEnabled ()) { - $this->createLog ( $log, $message, $transcript, $statusMessage, false, $transport ); - $this->writeToEmailLog ( $log ); + public function writeFailureLog( PostmanEmailLog $log, PostmanMessage $message = null, $transcript, PostmanModuleTransport $transport, $statusMessage ) { + if ( PostmanOptions::getInstance()->isMailLoggingEnabled() ) { + $this->createLog( $log, $message, $transcript, $statusMessage, false, $transport ); + $this->writeToEmailLog( $log ); } } - + /** * Writes an email sending attempt to the Email Log * * From http://wordpress.stackexchange.com/questions/8569/wp-insert-post-php-function-and-custom-fields */ - private function writeToEmailLog(PostmanEmailLog $log) { + private function writeToEmailLog( PostmanEmailLog $log ) { + + $this->checkForLogErrors( $log ); // nothing here is sanitized as WordPress should take care of // making database writes safe - $my_post = array ( + $my_post = array( 'post_type' => PostmanEmailLogPostType::POSTMAN_CUSTOM_POST_TYPE_SLUG, 'post_title' => $log->subject, 'post_content' => $log->body, 'post_excerpt' => $log->statusMessage, - 'post_status' => PostmanEmailLogService::POSTMAN_CUSTOM_POST_STATUS_PRIVATE + 'post_status' => PostmanEmailLogService::POSTMAN_CUSTOM_POST_STATUS_PRIVATE, ); - + // Insert the post into the database (WordPress gives us the Post ID) - $post_id = wp_insert_post ( $my_post ); - $this->logger->debug ( sprintf ( 'Saved message #%s to the database', $post_id ) ); - $this->logger->trace ( $log ); - + $post_id = wp_insert_post( $my_post ); + $this->logger->debug( sprintf( 'Saved message #%s to the database', $post_id ) ); + $this->logger->trace( $log ); + // Write the meta data related to the email - update_post_meta ( $post_id, 'success', $log->success ); - update_post_meta ( $post_id, 'from_header', $log->sender ); - if (! empty ( $log->toRecipients )) { - update_post_meta ( $post_id, 'to_header', $log->toRecipients ); + update_post_meta( $post_id, 'success', $log->success ); + update_post_meta( $post_id, 'from_header', $log->sender ); + if ( ! empty( $log->toRecipients ) ) { + update_post_meta( $post_id, 'to_header', $log->toRecipients ); } - if (! empty ( $log->ccRecipients )) { - update_post_meta ( $post_id, 'cc_header', $log->ccRecipients ); + if ( ! empty( $log->ccRecipients ) ) { + update_post_meta( $post_id, 'cc_header', $log->ccRecipients ); } - if (! empty ( $log->bccRecipients )) { - update_post_meta ( $post_id, 'bcc_header', $log->bccRecipients ); + if ( ! empty( $log->bccRecipients ) ) { + update_post_meta( $post_id, 'bcc_header', $log->bccRecipients ); } - if (! empty ( $log->replyTo )) { - update_post_meta ( $post_id, 'reply_to_header', $log->replyTo ); + if ( ! empty( $log->replyTo ) ) { + update_post_meta( $post_id, 'reply_to_header', $log->replyTo ); } - update_post_meta ( $post_id, 'transport_uri', $log->transportUri ); - - if (! $log->success || true) { + update_post_meta( $post_id, 'transport_uri', $log->transportUri ); + + if ( ! $log->success || true ) { // alwas add the meta data so we can re-send it - update_post_meta ( $post_id, 'original_to', $log->originalTo ); - update_post_meta ( $post_id, 'original_subject', $log->originalSubject ); - update_post_meta ( $post_id, 'original_message', $log->originalMessage ); - update_post_meta ( $post_id, 'original_headers', $log->originalHeaders ); + update_post_meta( $post_id, 'original_to', $log->originalTo ); + update_post_meta( $post_id, 'original_subject', $log->originalSubject ); + update_post_meta( $post_id, 'original_message', $log->originalMessage ); + update_post_meta( $post_id, 'original_headers', $log->originalHeaders ); } - + // we do not sanitize the session transcript - let the reader decide how to handle the data - update_post_meta ( $post_id, 'session_transcript', $log->sessionTranscript ); - + update_post_meta( $post_id, 'session_transcript', $log->sessionTranscript ); + // truncate the log (remove older entries) - $purger = new PostmanEmailLogPurger (); - $purger->truncateLogItems ( PostmanOptions::getInstance ()->getMailLoggingMaxEntries () ); + $purger = new PostmanEmailLogPurger(); + $purger->truncateLogItems( PostmanOptions::getInstance()->getMailLoggingMaxEntries() ); + } + + private function checkForLogErrors( PostmanEmailLog $log ) { + if ( $log->statusMessage && ! empty( $log->statusMessage ) ) { + mail( get_bloginfo( 'admin_email' ), __( 'Post SMTP email error', Postman::TEXT_DOMAIN ), $log->statusMessage ); + } + + if ( strpos( strtolower( $log->sessionTranscript ), 'error' ) !== false ) { + mail( get_bloginfo( 'admin_email' ), __( 'Post SMTP session transcript error', Postman::TEXT_DOMAIN ), $log->sessionTranscript ); + } } - + /** * Creates a Log object for use by writeToEmailLog() * - * @param PostmanMessage $message - * @param unknown $transcript - * @param unknown $statusMessage - * @param unknown $success - * @param PostmanModuleTransport $transport + * @param PostmanMessage $message + * @param unknown $transcript + * @param unknown $statusMessage + * @param unknown $success + * @param PostmanModuleTransport $transport * @return PostmanEmailLog */ - private function createLog(PostmanEmailLog $log, PostmanMessage $message = null, $transcript, $statusMessage, $success, PostmanModuleTransport $transport) { - if ($message) { - $log->sender = $message->getFromAddress ()->format (); - $log->toRecipients = $this->flattenEmails ( $message->getToRecipients () ); - $log->ccRecipients = $this->flattenEmails ( $message->getCcRecipients () ); - $log->bccRecipients = $this->flattenEmails ( $message->getBccRecipients () ); - $log->subject = $message->getSubject (); - $log->body = $message->getBody (); - if (null !== $message->getReplyTo ()) { - $log->replyTo = $message->getReplyTo ()->format (); + private function createLog( PostmanEmailLog $log, PostmanMessage $message = null, $transcript, $statusMessage, $success, PostmanModuleTransport $transport ) { + if ( $message ) { + $log->sender = $message->getFromAddress()->format(); + $log->toRecipients = $this->flattenEmails( $message->getToRecipients() ); + $log->ccRecipients = $this->flattenEmails( $message->getCcRecipients() ); + $log->bccRecipients = $this->flattenEmails( $message->getBccRecipients() ); + $log->subject = $message->getSubject(); + $log->body = $message->getBody(); + if ( null !== $message->getReplyTo() ) { + $log->replyTo = $message->getReplyTo()->format(); } } $log->success = $success; $log->statusMessage = $statusMessage; - $log->transportUri = PostmanTransportRegistry::getInstance ()->getPublicTransportUri ( $transport ); + $log->transportUri = PostmanTransportRegistry::getInstance()->getPublicTransportUri( $transport ); $log->sessionTranscript = $log->transportUri . "\n\n" . $transcript; return $log; } - + /** * Creates a readable "TO" entry based on the recipient header * - * @param array $addresses + * @param array $addresses * @return string */ - private static function flattenEmails(array $addresses) { + private static function flattenEmails( array $addresses ) { $flat = ''; $count = 0; foreach ( $addresses as $address ) { - if ($count >= 3) { - $flat .= sprintf ( __ ( '.. +%d more', Postman::TEXT_DOMAIN ), sizeof ( $addresses ) - $count ); + if ( $count >= 3 ) { + $flat .= sprintf( __( '.. +%d more', Postman::TEXT_DOMAIN ), sizeof( $addresses ) - $count ); break; } - if ($count > 0) { + if ( $count > 0 ) { $flat .= ', '; } - $flat .= $address->format (); + $flat .= $address->format(); $count ++; } return $flat; @@ -207,18 +219,18 @@ if (! class_exists ( 'PostmanEmailLogService' )) { } } -if (! class_exists ( 'PostmanEmailLogPurger' )) { +if ( ! class_exists( 'PostmanEmailLogPurger' ) ) { class PostmanEmailLogPurger { private $posts; private $logger; - + /** * * @return unknown */ function __construct() { - $this->logger = new PostmanLogger ( get_class ( $this ) ); - $args = array ( + $this->logger = new PostmanLogger( get_class( $this ) ); + $args = array( 'posts_per_page' => 1000, 'offset' => 0, 'category' => '', @@ -233,46 +245,46 @@ if (! class_exists ( 'PostmanEmailLogPurger' )) { 'post_mime_type' => '', 'post_parent' => '', 'post_status' => 'private', - 'suppress_filters' => true + 'suppress_filters' => true, ); - $this->posts = get_posts ( $args ); + $this->posts = get_posts( $args ); } - + /** * - * @param array $posts - * @param unknown $postid + * @param array $posts + * @param unknown $postid */ - function verifyLogItemExistsAndRemove($postid) { + function verifyLogItemExistsAndRemove( $postid ) { $force_delete = true; foreach ( $this->posts as $post ) { - if ($post->ID == $postid) { - $this->logger->debug ( 'deleting log item ' . intval($postid) ); - wp_delete_post ( $postid, $force_delete ); + if ( $post->ID == $postid ) { + $this->logger->debug( 'deleting log item ' . intval( $postid ) ); + wp_delete_post( $postid, $force_delete ); return; } } - $this->logger->warn ( 'could not find Postman Log Item #' . $postid ); + $this->logger->warn( 'could not find Postman Log Item #' . $postid ); } function removeAll() { - $this->logger->debug ( sprintf ( 'deleting %d log items ', sizeof ( $this->posts ) ) ); + $this->logger->debug( sprintf( 'deleting %d log items ', sizeof( $this->posts ) ) ); $force_delete = true; foreach ( $this->posts as $post ) { - wp_delete_post ( $post->ID, $force_delete ); + wp_delete_post( $post->ID, $force_delete ); } } - + /** * - * @param unknown $size + * @param unknown $size */ - function truncateLogItems($size) { - $index = count ( $this->posts ); + function truncateLogItems( $size ) { + $index = count( $this->posts ); $force_delete = true; while ( $index > $size ) { - $postid = $this->posts [-- $index]->ID; - $this->logger->debug ( 'deleting log item ' . $postid ); - wp_delete_post ( $postid, $force_delete ); + $postid = $this->posts [ -- $index ]->ID; + $this->logger->debug( 'deleting log item ' . $postid ); + wp_delete_post( $postid, $force_delete ); } } } diff --git a/Postman/Postman-Email-Log/PostmanEmailLogView.php b/Postman/Postman-Email-Log/PostmanEmailLogView.php index df885cd..f8f4fac 100644 --- a/Postman/Postman-Email-Log/PostmanEmailLogView.php +++ b/Postman/Postman-Email-Log/PostmanEmailLogView.php @@ -345,6 +345,7 @@ class PostmanEmailLogView extends WP_List_Table { /* Translators: where %s indicates the relative time from now */ $date = sprintf( _x( '%s ago', 'A relative time as in "five days ago"', Postman::TEXT_DOMAIN ), $humanTime ); } + $flattenedPost = array( // the post title must be escaped as they are displayed in the HTML output 'title' => esc_html( $post->post_title ), diff --git a/Postman/PostmanPluginFeedback.php b/Postman/PostmanPluginFeedback.php index 562d8a5..de7d73f 100644 --- a/Postman/PostmanPluginFeedback.php +++ b/Postman/PostmanPluginFeedback.php @@ -32,7 +32,7 @@ class PostmanPluginFeedback { if ( isset( $_POST['support'] ) ) { $payload['support']['email'] = sanitize_email( $_POST['support']['email'] ); - $payload['support']['title'] = sanitize_email( $_POST['support']['title'] ); + $payload['support']['title'] = sanitize_text_field( $_POST['support']['title'] ); $payload['support']['text'] = sanitize_textarea_field( $_POST['support']['text'] ); } @@ -40,7 +40,7 @@ class PostmanPluginFeedback { 'body' => $payload, ); $result = wp_remote_post( 'https://postmansmtp.com/feedback', $args ); - die(); + die( 'success' ); } function admin_head() { diff --git a/Postman/PostmanUtils.php b/Postman/PostmanUtils.php index 70a0ec4..fe7cbf4 100644 --- a/Postman/PostmanUtils.php +++ b/Postman/PostmanUtils.php @@ -10,13 +10,13 @@ class PostmanUtils { private static $logger; private static $emailValidator; - const POSTMAN_SETTINGS_PAGE_STUB = 'postman'; + const POSTMAN_SETTINGS_PAGE_STUB = 'postman'; const REQUEST_OAUTH2_GRANT_SLUG = 'postman/requestOauthGrant'; const POSTMAN_EMAIL_LOG_PAGE_STUB = 'postman_email_log'; // redirections back to THIS SITE should always be relative because of IIS bug - const POSTMAN_EMAIL_LOG_PAGE_RELATIVE_URL = 'tools.php?page=postman_email_log'; - const POSTMAN_HOME_PAGE_RELATIVE_URL = 'options-general.php?page=postman'; + const POSTMAN_EMAIL_LOG_PAGE_RELATIVE_URL = 'admin.php?page=postman_email_log'; + const POSTMAN_HOME_PAGE_RELATIVE_URL = 'admin.php?page=postman'; // custom admin post page const ADMIN_POST_OAUTH2_GRANT_URL_PART = 'admin-post.php?action=postman/requestOauthGrant'; @@ -36,7 +36,7 @@ class PostmanUtils { * @return string */ public static function getPageUrl( $slug ) { - return get_admin_url() . 'options-general.php?page=' . $slug; + return get_admin_url() . 'admin.php?page=' . $slug; } /** diff --git a/Postman/PostmanViewController.php b/Postman/PostmanViewController.php index 2ce4d2b..9dd901d 100644 --- a/Postman/PostmanViewController.php +++ b/Postman/PostmanViewController.php @@ -56,7 +56,7 @@ if ( ! class_exists( 'PostmanViewController' ) ) { $this, 'outputDefaultContent', ); - $mainPostmanSettingsPage = add_options_page( $pageTitle, $pluginName, Postman::MANAGE_POSTMAN_CAPABILITY_NAME, $uniqueId, $pageOptions ); + $mainPostmanSettingsPage = add_menu_page( $pageTitle, $pluginName, Postman::MANAGE_POSTMAN_CAPABILITY_NAME, $uniqueId, $pageOptions ); // When the plugin options page is loaded, also load the stylesheet add_action( 'admin_print_styles-' . $mainPostmanSettingsPage, array( $this, diff --git a/postman-smtp.php b/postman-smtp.php index 21a3911..04b8e16 100644 --- a/postman-smtp.php +++ b/postman-smtp.php @@ -4,7 +4,7 @@ * Plugin Name: Post SMTP * Plugin URI: https://wordpress.org/plugins/post-smtp/ * Description: Email not reliable? Post SMTP is the first and only WordPress SMTP plugin to implement OAuth 2.0 for Gmail, Hotmail and Yahoo Mail. Setup is a breeze with the Configuration Wizard and integrated Port Tester. Enjoy worry-free delivery even if your password changes! - * Version: 1.7.7 + * Version: 1.7.8 * Author: Jason Hendriks, Yehuda Hassine * Text Domain: post-smtp * Author URI: https://postmansmtp.com @@ -2,8 +2,8 @@ Contributors: yehudah, jasonhendriks Tags: postman smtp, postman, smtp, email, mail, mailer, email log, oauth2, gmail, google apps, hotmail, yahoo, mandrill api, sendgrid api, elastic email Requires at least: 3.9 -Tested up to: 4.8 -Stable tag: 1.7.7 +Tested up to: 4.9 +Stable tag: 1.7.8 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -281,11 +281,15 @@ To avoid being flagged as spam, you need to prove your email isn't forged. On a == Changelog == -= 1.7.8 - 2017-10-28 += 1.7.8 - 2017-11-17 +* = Menu Items grouping = * Fixed: IP detection error in some web hosts * Fixed: Link open in new page attribute = _blank * Fixed: Replace deprecated PHP 7 functions. * Updated: Validator TLD's list +* Added: Email log date and search filter. +* Added: Alert on sending error (Fallback to local mail) +* Added: Email body preview (not raw) = 1.7.7 - 2017-10-17 * Fixed: Error sending files with sendgrid |