Giter Site home page Giter Site logo

omnimail's Introduction

Build Status Scrutinizer Code Quality Code Coverage Latest Stable Version Join the chat at https://gitter.im/omnimail/omnimail

Send email across all platforms using one interface.

Table Of Content

  1. Requirements
  2. Installation
  3. Providers
  4. Email
  5. Mass Mailings
  6. Factory
  7. Exceptions
  8. Logging
  9. License

Requirements

This library uses PHP 5.6 and greater version.

Installation

It is recommended that you install the Omnimail library through composer. To do so, run the Composer command to install the latest stable version of Omnimail library.

composer require omnimail/omnimail

Providers

AmazonSES

Installation

To use the AmazonSES mailer class, you will need to install the daniel-zahariev/php-aws-ses library using composer.

composer require "daniel-zahariev/php-aws-ses:^0.9.2"

Usage

use Omnimail\Email;
use Omnimail\AmazonSES;

$mailer = new AmazonSES($accessKey, $secretKey, $region, $verifyPeer, $verifyHost, $signatureVersion);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Mailgun

Installation

To use the Mailgun mailer class, you will need to install the mailgun/mailgun-php library using composer. You do also need to install a HTTP client that sends messages. You can use any client that provided the virtual package php-http/client-implementation

composer require mailgun/mailgun-php php-http/guzzle6-adapter

Usage

use Omnimail\Email;
use Omnimail\Mailgun;

$mailer = new Mailgun($apiKey, $domain);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Mailjet

Installation

To use the Mailjet mailer class, you will need to install the mailjet/mailjet-apiv3-php library using composer.

composer require mailjet/mailjet-apiv3-php

Usage

use Omnimail\Email;
use Omnimail\Mailjet;

$mailer = new Mailjet($apikey, $apisecret);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Mandrill

Installation

To use the Mandrill mailer class, you will need to install the mandrill/mandrill library using composer.

composer require mandrill/mandrill

Usage

use Omnimail\Email;
use Omnimail\Mandrill;

$mailer = new Mandrill($apiKey);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Postmark

Installation

To use the Postmark mailer class, you will need to install the wildbit/postmark-php library using composer.

composer require wildbit/postmark-php

Usage

use Omnimail\Email;
use Omnimail\Postmark;

$mailer = new Postmark($serverApiToken);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Sendgrid

Installation

To use the Sendgrid mailer class, you will need to install the sendgrid/sendgrid library using composer.

composer require sendgrid/sendgrid

Usage

use Omnimail\Email;
use Omnimail\Sendgrid;

$mailer = new Sendgrid($apiKey);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

SendinBlue

Installation

To use the SendinBlue mailer class, you will need to install the mailin-api/mailin-api-php library using composer.

composer require mailin-api/mailin-api-php

Usage

use Omnimail\Email;
use Omnimail\SendinBlue;

$mailer = new SendinBlue($accessKey);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

SMTP

Installation

To use the SMTP mailer class, you will need to install the phpmailer/phpmailer library using composer.

composer require phpmailer/phpmailer

Usage

use Omnimail\Email;
use ShahariaAzam\SMTPMailer\SMTPMailer;

$mailer = new SMTPMailer("SMTP HOSTNAME", "SMTP USERNAME", "SMTP PASSWORD");

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Gmail

Installation

To use the Gmail mailer class, you will need to install the phpmailer/phpmailer library using composer.

composer require phpmailer/phpmailer

Usage

use Omnimail\Email;
use Omnimail\Gmail;

$mailer = new Gmail("[email protected]", "password", []);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);

Email

An Email object implements the EmailInterface inteface. You can create your own Email class and send it to any mailer if it implements the EmailInterface inteface.

To

The To property of the email is for defining the recipients of the email. You can set multiple recipients.

$email = new Email();
$email->addTo('[email protected]', 'Recipient1 Name');
$email->addTo('[email protected]', 'Recipient2 Name');

From

The From property of the email is for defining the mailer of the email.

$email = new Email();
$email->setFrom('[email protected]', 'Sender Name');

CC

Like the To property, the CC property can have multiple recipients.

$email = new Email();
$email->addCc('[email protected]', 'Recipient1 Name');
$email->addCc('[email protected]', 'Recipient2 Name');

BCC

Like the To property, the BCC property can have multiple recipients.

$email = new Email();
$email->addBcc('[email protected]', 'Recipient1 Name');
$email->addBcc('[email protected]', 'Recipient2 Name');

Reply To

The Reply To property of the email is for defining the email that should receive responses.

$email = new Email();
$email->setReplyTo('[email protected]', 'Sender Name');

Subject

The Subject property of the email is for defining the subject of the email.

$email = new Email();
$email->setSubject('Hello, World!');

Text Body

The Text Body property of the email is for defining the text body of the email.

$email = new Email();
$email->setTextBody('This is plain text.');

HTML Body

The HTML Body property of the email is for defining the HTML body of the email.

$email = new Email();
$email->setHtmlBody('<h1>Hi!</h1><p>This is HTML!</p>');

Attachments

The Attachments property of the email is for joining attachments to the email.

Example using string as content

use Omnimail\Email;
use Omnimail\Attachment;

$attachment = new Attachment();
$attachment->setName('my_file.txt');
$attachment->setMimeType('text/plain');
$attachment->setContent('This is plain text');

$email = new Email();
$email->addAttachment($attachment);

Example using file path as content

use Omnimail\Email;
use Omnimail\Attachment;

$attachment = new Attachment();
$attachment->setMimeType('text/plain');
$attachment->setPath(__DIR__ . '/my_file.txt');

$email = new Email();
$email->addAttachment($attachment);

Inline attachments

use Omnimail\Email;
use Omnimail\Attachment;

$attachment = new Attachment();
$attachment->setPath(__DIR__ . '/image.png');
$attachment->setContentId('image.png');

$email = new Email();
$email->setHtmlBody('<p>Hello!</p><img src="cid:image.png">');
$email->addAttachment($attachment);

Factory

Alternatively, you can use the factory method to create a mailer. Consider the following example to create a AmazonSES mailer:

use Omnimail\Omnimail;

$mailer = Omnimail::create(Omnimail::AMAZONSES, ['accessKey' => $accessKey, 'secretKey' => $secretKey]);

Mass Mailings

The mass mailing component is for interacting with mass mailing providers. Currently the code focusses on data retrieval, but in future it should also define creating and sending mass mailings.

There are 2 functions currently described for the Mass mailings interface getMailings and getRecipients.

getMailings

    $mailer = Omnimail::create('Silverpop', array('credentials' => new Credentials(array('username' => $userName...)))->getMailings();
    $mailer->setStartTimeStamp(strtotime('7 days ago'));
    $mailer->setEndTimeStamp(strtotime('now'));
    // Instead of using set methods a Credentials object can be passed in ie.
    // Omnimail::create('Silverpop', array('credentials' => new Credentials(array('username' => $userName...)));
    // The advantage of using the Credentials object is that the object will not disclose
    // the credentials when var_dump or similar is called, helping to make the code
    // more secure.

    $mailings = $mailer->getResponse();
    for ($i = 0; $i < 15; $i++) {
      if (!$mailings->isCompleted()) {
        sleep(15);
      }
      else {
        foreach (\Omnimail\Common\Responses\BaseResponse $mailings as \Omnimail\Common\Responses\Mailing $mailing) {

           $detail => array(
             'subject' => $mailingโ†’getSubject(),
             'external_identifier' => $mailing->getMailingIdentifier(),
             'name' => $mailing->getName(),
             'scheduled_date' => $mailing->getScheduledDate(),
             'start_date' => $mailing->getSendStartDate(),
             'number_sent' => $mailing->getNumberSent(),
             'body_html' => $mailing->getHtmlBody(),
             'body_text' => $mailingโ†’getTextBody(),
             // Note that in the case of Silverpop these statistics are not retrieved by the
             // same api call. This is invisible to the calling function, and unless
             // stats are requested they are not retrieved.
             'number_bounced' => $mailing->getNumberBounces(),
             'number_opened_total' => $mailing->getNumberOpens(),
             'number_opened_unique' => $mailing->getNumberUniqueOpens(),
             'number_unsubscribed' => $mailing->getNumberUnsubscribes(),
             'number_suppressed' => $mailing->getNumberSuppressedByProvider(),
             // 'forwarded'
             'number_blocked' => $mailing->getNumberBlocked(),
             // 'clicked_total' => $stats['NumGrossClick'],
             'number_abuse_complaints' => $mailing->getNumberAbuseReports(),
            );
          }
      }
    }

getMailings

    $mailer = Omnimail::create('Silverpop')->getRecipients();
    $mailer->setUserName($userName);
    $mailer->setPassword($password);
    $mailer->setStartTimeStamp(strtotime('7 days ago'));
    $mailer->setEndTimeStamp(strtotime('now'));
    $mailer->setMailingIdentifier(123);

    $recipients = $mailer->getResponse();
    if (!$recipients->isCompleted()) {
      // sleep or exit & retry later.
    }

    foreach (\Omnimail\Responses\RecipientsResponse $recipients as \Omnimail\Responses\Recipient $$recipient) {

     $detail => array(
       'external_identifier' => $mailing->getMailingIdentifier(),
       'email' =>  $mailing->getEmail(),
       'provider_contact_id' => $mailing->getContactIdentifier(),
       'contact_id' => $mailing->GetContactReference(),
        // Const ACTION_SENT = โ€˜Sentโ€™, ACTION_OPENED = โ€˜Openโ€™, ...
       'action' => $mailing->getRecipientAction(),
       'timestamp' => getRecipientActionTimestamp(),
     );

Exceptions

Failures to send emails will throw exceptions.

Exceptions

  • Omnimail\Exception\Exception
  • Omnimail\Exception\EmailDeliveryException
  • Omnimail\Exception\InvalidRequestException
  • Omnimail\Exception\UnauthorizedException
  • Omnimail\Exception\MailerNotFoundException

To catch all exception, consider the following.

try {
    $mailer->send($email);
} catch (\Omnimail\Exception\Exception $e) {
    echo 'Something went wrong: ' . $e->getMessage();
}

To catch specific exceptions, consider the following.

try {
    $mailer->send($email);
} catch (\Omnimail\Exception\UnauthorizedException $e) {
    echo 'Your credentials must be incorrect';
} catch (\Omnimail\Exception\InvalidRequestException $e) {
    echo 'The request is badly formatted, check that all required fields are filled.';
} catch (\Omnimail\Exception\EmailDeliveryException $e) {
    echo 'The email did not go out.';
}

Logging

All mailers constructors take a PSR-3 compatible logger.

Email sent (including the email) are logged at INFO level. Errors (including the email) are reported at the ERROR level.

Example using Monolog

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Omnimail\Mailgun;

$logger = new Logger('name');
$logger->pushHandler(new StreamHandler('path/to/your.log', Logger::INFO));

$mailer = new Mailgun($apiKey, $domain, $logger);
$mailer->send($email);

License

Omnimail is licensed under The MIT License (MIT).

omnimail's People

Contributors

bretto36 avatar daarond avatar eileenmcnaughton avatar gabrielbull avatar nenillo avatar nyholm avatar shahariaazam avatar stevecoug avatar suhaboncukcu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

omnimail's Issues

Proposal to make some improvements

As this library is growing and developer are showing interest, it would be great if we can re-factor something (no new feature, improvement on rapid improvement).

I would like to propose few points here:

  • We need to improve tests as much as possible to make sure everything is working properly after changes.

  • We can remove travis and use GitHub workflow for CI to code checks, tests, etc.

  • We shouldn't accept any PR without tests and pass the CI workflow

  • Development related tooling is not much available for php5.5. Also its not being maintained anymore. So to keep minimum compatibility to php5.6

  • Bug fix release we can make frequently but we need to focus on development branch for major release.

  • All PR must be submitted to development branch and we will merge to merge based on milestone. So it will reduce frequent release and give developers some resting time to upgrade for every PR.

Most of the related changes has been prepared in my draft PR #46.

I would like to know your feedback before making any decision.

Also please feel free to add your own proposal.

Thanks in advance.

Backward Compatibility - Scalar type hinting is supported only from PHP >= 7.0

Here is the constructor of AmazonSES from https://github.com/gabrielbull/omnimail/blob/master/src/AmazonSES.php#L137

public function __construct($accessKey = null, $secretKey = null, $host = self::AWS_US_EAST_1, $verifyPeer = true, $verifyHost = true, LoggerInterface $logger = null, string $signatureVersion = SimpleEmailService::REQUEST_SIGNATURE_V4)
{
    $this->verifyPeer = $verifyPeer;
    $this->verifyHost = $verifyHost;
    $this->accessKey = $accessKey;
    $this->secretKey = $secretKey;
    $this->host = $host;
    $this->logger = $logger;
    $this->signatureVersion = $signatureVersion;
}

In the last argument string $signatureVersion = SimpleEmailService::REQUEST_SIGNATURE_V4. Scalary type hinting on argument is not supported in PHP5.5 as this library is supposed to support PHP5.5 too.

It seems that in the recent PR #40 that changes were made. But it will break the backward compatibility for PHP5.5.

We should fix it ASAP.

Guzzle should be required directly

What is the benefit to requiring php-http via the Guzzle adapter over requiring Guzzle directly? Seems like it only adds more dependencies.

Future plans

I'm wanting to check in on future plans for this extension.

Currently the minimum php version is set to 5.6 and the current supported versions 8.0 and 8.1 are not being run under CI https://www.php.net/supported-versions.php. I would suggest that a minimum version of 7.2 (unsupported as of a year ago) or 7.3 (only just unsupported) would allow keeping up to date with packages and coding style and make it easier to add support for newer versions.

I also have questions about functionality. The functionality gaps I'm hitting are

  • inability to set some parameters on smtp mailer - I did just now move my PR on that from the old repo to this one
  • wanting to extend the mass mailing functionality - eg. currently there are functions to retrieve recipient lists but not to create them. I can PR in some of this functionality if people are open to it but otherwise I probably need to think about other approaches in our codebase.

Roadmap....

Hi,

I was looking for something like this that took an Omnipay-style approach to Mail integration & this looks good. The only thing is .. there is no actual overlap between what your package is doing & what I am working on - I'm going to paste it up here anyway in case there is some synergy - as I feel like you have the starting point for more comprehensive integration with mailing providers.

The way I expect my calling code to look is something like

$mailingIntegration = new Omnimail::create('Silverpop', $credentials);
$mailings = $mailingIntegration->getMailings(array('start_date' => '2017-01-01'));

foreach ($mailings as $mailing) {
  $html = $mailing->getHtml();
  $subject = $mailing->getSubject();
  $text = $mailing->getText();
  // this is their unique ID
  $theirIdentifier = $mailing->getProviderIdentifier();

  $ourIdentifier = $mailing->getIndentifier();
  $name = $mailing->getName();
  $created_date = $mailing->getCreatedDate();
  $scheduled_date = $mailing->getScheduledDate();

  $numberSent = $mailing->getNumberSent();
  $numberDelivered = $mailing->getNumberDelivered();
    // e.g spam
  $numberBounced = $mailing->getNumberBounced();
  $numberBlocked = $mailing->getNumberBlocked();
  $numberUnsubscribe = $mailing->getNumberUnsubscribed();
  $numberComplaints = $mailing->getNumberAbuseReports();
  $numberOpens = $mailing->getNumberOpens();
  $numberUniqueOpens = $mailing->getNumberUniqueOpens();
  $numberClicks = $mailing->getNumberClicks();
  $numberUniqueClicks = $mailing->getNumberUniqueClicks();

  $bounces = $mailing->getBounces();
  foreach ($bounces as $bounce) {
    $recipient = $bounce->getRecipient();
    $ourIdentifier= $recipient->getIdentifier();
    // ie identifier for the provider
    $theirIdentifier = $recipient->getProviderIdentifier();
    $emailAddress = $recipient->getEmail();
    $bounceType = $bounce->getBounceType();
    
  }
}

SendGrid Bad Gateway

I'm getting a bad gateway error when sending an e-mail with SendGrid. I've confirmed that:

  1. The error occurs with the example code provided in README.md
  2. The error doesn't occur when using the bare SendGrid library.
  3. The error occurs on multiple machines.

I can reproduce as follows:

$ composer require sendgrid/sendgrid
$ composer require omnimail/omnimail
/* test1.php */
<?php
require_once (__DIR__ . '/vendor/autoload.php');

use Omnimail\Email;
use Omnimail\Sendgrid;

$mailer = new Sendgrid(MY_REAL_LIFE_SG_KEY);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('Hello, world!')
    ->setTextBody('Hello World! How are you?');

$mailer->send($email);
/* test2.php */
<?php
require_once (__DIR__ . '/vendor/autoload.php');

$from = new SendGrid\Email("Example User", "[email protected]");
$subject = "Sending with SendGrid is Fun";
$to = new SendGrid\Email("Example User", "[email protected]");
$content = new SendGrid\Content("text/plain", "and easy to do anywhere, even with PHP");
$mail = new SendGrid\Mail($from, $subject, $to, $content);

$sg = new \SendGrid(MY_REAL_LIFE_SG_KEY);

$response = $sg->client->mail()->send()->post($mail);
echo $response->statusCode();
print_r($response->headers());
echo $response->body();
$ php test1.php
PHP Fatal error:  Uncaught exception 'Omnimail\Exception\InvalidRequestException' in C:\Users\jschilz\test\vendor\omnimail\omnimail\src\Sendgri
d.php:137
Stack trace:
#0 C:\Users\jschilz\test\test1.php(15): Omnimail\Sendgrid->send(Object(Omnimail\Email))
#1 {main}
  thrown in C:\Users\jschilz\test\vendor\omnimail\omnimail\src\Sendgrid.php on line 137

Fatal error: Uncaught exception 'Omnimail\Exception\InvalidRequestException' in C:\Users\jschilz\test\vendor\omnimail\omnimail\src\Sendgrid.php
 on line 137

Omnimail\Exception\InvalidRequestException:  in C:\Users\jschilz\test\vendor\omnimail\omnimail\src\Sendgrid.php on line 137

Call Stack:
    0.0002     124920   1. {main}() C:\Users\jschilz\test\test1.php:0
    0.0056     425304   2. Omnimail\Sendgrid->send() C:\Users\jschilz\test\test1.php:15

I put some debugging statements inside SendGrid.php to determine that SendGrid is returning a 502 Bad Gateway response.

$ php test2.php
202Array
(
    [0] => HTTP/1.1 202 Accepted
    [1] => Server: nginx
    [2] => Date: Tue, 23 Jan 2018 00:26:40 GMT
    [3] => Content-Type: text/plain; charset=utf-8
    [4] => Content-Length: 0
    [5] => Connection: keep-alive
    [6] => X-Message-Id: AFEhnCqnT46tLy-1FAdm-A
    [7] => Access-Control-Allow-Origin: https://sendgrid.api-docs.io
    [8] => Access-Control-Allow-Methods: POST
    [9] => Access-Control-Allow-Headers: Authorization, Content-Type, On-behalf-of, x-sg-elas-acl
    [10] => Access-Control-Max-Age: 600
    [11] => X-No-CORS-Reason: https://sendgrid.com/docs/Classroom/Basics/API/cors.html
    [12] =>
    [13] =>
)

I can work to trouble shoot this tomorrow, but I thought I'd post the issue tonight incase the solution is obvious to you. (:

Feature proposal: logging only mailer

For debugging and development purposes, it would be helpful for me to have a mailer which does not send any emails. Instead, it would implement only the logging features of a mailer. The calling code could provide this mailer a logger which logs to the console, or to a file, etc.

I would propose to call this mailer class NoMail, implementing the MailerInterface. Per your spec, it would log emails at info level.

If you concur, I'll submit a pull request. Otherwise, please close. (:

AmazonSES needs support for signature version 4

From an email I received today:

Amazon Web Services currently supports Amazon SES API requests that are signed using Signature Version 3 and Signature Version 4 processes. Signature Version 4 further enhances the security around authentication and authorization of Amazon SES customers by using a signing key instead of your secret access key. To improve the security for our customers, beginning October 1, 2020, Amazon Signature Version 3 will be turned off (deprecated) in Amazon SES in favor of Signature Version 4.

Amazon SES customers who are still using Signature Version 3 must migrate to Signature Version 4 by September 30, 2020. After that, Amazon SES will only accept requests that are signed using Signature Version 4. For more information, see Signature Version 4 signing process [1].

What Happens if I Don't Make Updates?

Requests signed with Signature Version 3 that are made after September 30, 2020 will fail to authenticate with Amazon SES. Requesters will see errors stating that the request must be signed with Signature Version 4.

References:
[1] https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html

Monolog Error: Fatal error: Uncaught TypeError: Argument 2 passed to Monolog\Logger::info()

I'm receiving the below error when trying to use Monolog whilst following your instructions, but replacing Mailgun with SendGrid.

Fatal error: Uncaught TypeError: Argument 2 passed to Monolog\Logger::info() must be of the type array, object given, called in /vendor/omnimail/omnimail/src/Sendgrid.php on line 94 and defined in vendor/monolog/monolog/src/Monolog/Logger.php:543 Stack trace: #0 /vendor/omnimail/omnimail/src/Sendgrid.php(94): Monolog\Logger->info('Email sent: 'Ne...', Object(Omnimail\Email)) #1/enquiry.php(47): Omnimail\Sendgrid->send(Object(Omnimail\Email)) #2 {main} thrown in /vendor/monolog/monolog/src/Monolog/Logger.php on line 543

My code:

$logger = new Logger('name');
$logger->pushHandler(new StreamHandler('errors.log', Logger::INFO));

$sender = new Sendgrid($apiKey, $logger);

$email = (new Email())
    ->addTo('[email protected]')
    ->setFrom('[email protected]')
    ->setSubject('New Enquiry')
    ->setHtmlBody($emailHtml);

$sender->send($email);

I'm not sure if I'm missing something or if you have a quick fix for the issue in the Omnimail\Sendgrid class?

Thanks in advance!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.