-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 90f8fda
Showing
6 changed files
with
265 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
vendor/ | ||
composer.lock | ||
phpunit.xml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Symfony Mailer for Microsoft | ||
|
||
Use this bridge to send Emails with `symfony/mailer` and Microsoft Graph API. Supported Features: | ||
|
||
|
||
|
||
| Field | Supported | | ||
|-------------|-----------| | ||
| Subject | ✓ | | ||
| Return-Path | ✕ | | ||
| Sender | ✓ | | ||
| From | ✕ | | ||
| Reply-To | ✕ | | ||
| To | ✓ | | ||
| Cc | ✓ | | ||
| Bcc | ✓ | | ||
| Priority | ✕ | | ||
| Text | ✓ | | ||
| HTML | ✕ | | ||
| Attachments | ✓ | | ||
|
||
|
||
## Configuration | ||
|
||
Extend your `services.yaml`configuration | ||
|
||
PMDevelopment\Mailer\Bridge\Microsoft\Transport\GraphTransportFactory: | ||
tags: | ||
- { name: mailer.transport_factory } | ||
|
||
|
||
Add required Parameters to `.env` | ||
|
||
MAILER_DSN=microsoft+graph://clientId:clientSecret@tenantId | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace PMDevelopment\Mailer\Bridge\Microsoft\Transport; | ||
|
||
use Microsoft\Graph\Graph; | ||
use Symfony\Component\Mailer\Transport\Dsn; | ||
|
||
class GraphClient | ||
{ | ||
private Graph $graph; | ||
|
||
private string $clientId; | ||
private string $clientSecret; | ||
private string $tenantId; | ||
|
||
public function __construct(string $accessToken, Dsn $dsn) | ||
{ | ||
$this->graph = new Graph(); | ||
$this->graph->setAccessToken($accessToken); | ||
|
||
$this->clientId = $dsn->getUser(); | ||
$this->clientSecret = $dsn->getPassword(); | ||
$this->tenantId = $dsn->getHost(); | ||
} | ||
|
||
public function getGraph(): Graph | ||
{ | ||
return $this->graph; | ||
} | ||
|
||
public function getClientId(): string | ||
{ | ||
return $this->clientId; | ||
} | ||
|
||
public function getClientSecret(): string | ||
{ | ||
return $this->clientSecret; | ||
} | ||
|
||
public function getTenantId(): string | ||
{ | ||
return $this->tenantId; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
<?php | ||
|
||
namespace PMDevelopment\Mailer\Bridge\Microsoft\Transport; | ||
|
||
use GuzzleHttp\Client; | ||
use Microsoft\Graph\Graph; | ||
use Psr\Log\LoggerInterface; | ||
use RuntimeException; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\Mailer\Exception\HttpTransportException; | ||
use Symfony\Component\Mailer\Exception\RuntimeException as MailerRuntimeException; | ||
use Symfony\Component\Mailer\SentMessage; | ||
use Symfony\Component\Mailer\Transport\AbstractTransport; | ||
use Symfony\Component\Mime\Address; | ||
use Symfony\Component\Mime\MessageConverter; | ||
use Symfony\Component\Mime\Part\DataPart; | ||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; | ||
use Throwable; | ||
|
||
class GraphSendMailTransport extends AbstractTransport | ||
{ | ||
private GraphClient $client; | ||
|
||
public function __construct(GraphClient $client, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) | ||
{ | ||
$this->client = $client; | ||
|
||
parent::__construct($dispatcher, $logger); | ||
} | ||
|
||
|
||
protected function doSend(SentMessage $message): void | ||
{ | ||
$graph = $this->client->getGraph(); | ||
|
||
try { | ||
$email = MessageConverter::toEmail($message->getOriginalMessage()); | ||
} catch (Throwable $e) { | ||
throw new MailerRuntimeException(sprintf('Unable to send message with the "%s" transport: ', __CLASS__) . $e->getMessage(), 0, $e); | ||
} | ||
|
||
$mailBody = [ | ||
'Message' => [ | ||
'attachments' => [], | ||
'bccRecipients' => $this->getArrayFromAddresses($email->getBcc()), | ||
'body' => [ | ||
'contentType' => 'text', | ||
'content' => $email->getTextBody(), | ||
], | ||
'ccRecipients' => $this->getArrayFromAddresses($email->getCc()), | ||
'subject' => $email->getSubject(), | ||
'toRecipients' => $this->getArrayFromAddresses($email->getTo()), | ||
], | ||
]; | ||
|
||
foreach ($email->getAttachments() as $attachment) { | ||
$mailBody['Message']['attachments'][] = [ | ||
'@odata.type' => '#microsoft.graph.fileAttachment', | ||
'contentType' => 'text/plain', | ||
'contentBytes' => base64_encode($attachment->getBody()), | ||
'name' => $attachment->getPreparedHeaders()->getHeaderParameter('Content-Disposition', 'name'), | ||
]; | ||
} | ||
|
||
|
||
$result = $graph->createRequest(Request::METHOD_POST, sprintf('/users/%s/sendMail', $message->getEnvelope()->getSender()->getAddress())) | ||
->attachBody($mailBody) | ||
->execute(); | ||
|
||
if (Response::HTTP_ACCEPTED !== $result->getStatus()) { | ||
throw new MailerRuntimeException(sprintf('Sending mail failed with status %d', $result->getStatus())); | ||
} | ||
} | ||
|
||
public function __toString() | ||
{ | ||
return sprintf('microsoft+graph://%s:%s@%s', $this->client->getClientId(), $this->client->getClientSecret(), $this->client->getTenantId()); | ||
} | ||
|
||
private function getArrayFromAddresses(array $addresses) | ||
{ | ||
return array_map(function (Address $address) { | ||
return [ | ||
'emailAddress' => [ | ||
'address' => $address->getAddress(), | ||
], | ||
]; | ||
}, $addresses); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<?php | ||
|
||
namespace PMDevelopment\Mailer\Bridge\Microsoft\Transport; | ||
|
||
use GuzzleHttp\Client; | ||
use Microsoft\Graph\Graph; | ||
use RuntimeException; | ||
use Psr\Log\LoggerInterface; | ||
use Symfony\Component\Mailer\Exception\IncompleteDsnException; | ||
use Symfony\Component\Mailer\Exception\UnsupportedSchemeException; | ||
use Symfony\Component\Mailer\Transport\AbstractTransportFactory; | ||
use Symfony\Component\Mailer\Transport\Dsn; | ||
use Symfony\Component\Mailer\Transport\TransportInterface; | ||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; | ||
|
||
class GraphTransportFactory extends AbstractTransportFactory | ||
{ | ||
public function __construct(EventDispatcherInterface $eventDispatcher, LoggerInterface $logger = null) | ||
{ | ||
parent::__construct($eventDispatcher, null, $logger); | ||
} | ||
|
||
|
||
public function create(Dsn $dsn): TransportInterface | ||
{ | ||
$client = new Client(); | ||
|
||
$loginUrl = sprintf('https://login.microsoftonline.com/%s/oauth2/v2.0/token', $dsn->getHost()); | ||
|
||
$loginFormParameters = [ | ||
'client_id' => $dsn->getUser(), | ||
'client_secret' => $dsn->getPassword(), | ||
'grant_type' => 'client_credentials', | ||
'scope' => 'https://graph.microsoft.com/.default', | ||
]; | ||
|
||
$request = $client->post($loginUrl, [ | ||
'form_params' => $loginFormParameters, | ||
]); | ||
|
||
$response = json_decode($request->getBody()->getContents(), true); | ||
if (false === array_key_exists('access_token', $response)) { | ||
throw new RuntimeException('Key "access_token" not found in %s', implode(',', array_keys($response))); | ||
} | ||
|
||
return new GraphSendMailTransport(new GraphClient($response['access_token'], $dsn), $this->dispatcher, $this->logger); | ||
} | ||
|
||
|
||
protected function getSupportedSchemes(): array | ||
{ | ||
return ['microsoft+graph']; | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "pmdevelopment/microsoft-symfony-mailer", | ||
"description": "Microsoft Graph API bridge for symfony mailer", | ||
"keywords": [], | ||
"homepage": "https://pmdevelopment.de", | ||
"license": "MIT", | ||
"authors": [ | ||
{ | ||
"name": "Sven Joder", | ||
"email": "[email protected]" | ||
} | ||
], | ||
"require": { | ||
"php": "^7.4|^8.1", | ||
"guzzlehttp/guzzle": "^6.0|^7.0", | ||
"microsoft/microsoft-graph": "^1.66", | ||
"symfony/event-dispatcher-contracts": "*", | ||
"symfony/mailer": "^4.4.21|^5.2.6|^6.0", | ||
"symfony/symfony": "^4.4|^5.4|^6.0" | ||
}, | ||
"require-dev": { | ||
"symfony/http-client": "^4.4|^5.0|^6.0" | ||
}, | ||
"autoload": { | ||
"psr-4": { | ||
"PMDevelopment\\Mailer\\Bridge\\Microsoft\\": "" | ||
}, | ||
"exclude-from-classmap": [ | ||
"/Tests/" | ||
] | ||
}, | ||
"minimum-stability": "dev" | ||
} |