diff --git a/ProcessMaker/Mail/MicrosoftGraphMessageConverter.php b/ProcessMaker/Mail/MicrosoftGraphMessageConverter.php new file mode 100644 index 0000000000..e95a3ea281 --- /dev/null +++ b/ProcessMaker/Mail/MicrosoftGraphMessageConverter.php @@ -0,0 +1,55 @@ +getHtmlBody(); + $textBody = $email->getTextBody(); + + $message = [ + 'subject' => $email->getSubject() ?? '', + 'body' => [ + 'contentType' => $htmlBody !== null ? 'HTML' : 'Text', + 'content' => $htmlBody ?? $textBody ?? '', + ], + 'toRecipients' => self::convertAddresses($email->getTo()), + ]; + + $ccRecipients = self::convertAddresses($email->getCc()); + if ($ccRecipients) { + $message['ccRecipients'] = $ccRecipients; + } + + $bccRecipients = self::convertAddresses($email->getBcc()); + if ($bccRecipients) { + $message['bccRecipients'] = $bccRecipients; + } + + return [ + 'message' => $message, + 'saveToSentItems' => true, + ]; + } + + /** + * @param Address[] $addresses + */ + private static function convertAddresses(array $addresses): array + { + return array_map(function (Address $address) { + $emailAddress = ['address' => $address->getAddress()]; + + if ($address->getName()) { + $emailAddress['name'] = $address->getName(); + } + + return ['emailAddress' => $emailAddress]; + }, $addresses); + } +} diff --git a/ProcessMaker/Mail/MicrosoftGraphTokenProvider.php b/ProcessMaker/Mail/MicrosoftGraphTokenProvider.php index 913274bea6..1b97e5af05 100644 --- a/ProcessMaker/Mail/MicrosoftGraphTokenProvider.php +++ b/ProcessMaker/Mail/MicrosoftGraphTokenProvider.php @@ -15,7 +15,7 @@ class MicrosoftGraphTokenProvider public function __construct( private array $config, private int|string $serverIndex = 0, - private ?Client $httpClient = null, + private ?Client $httpClient = null ) { } diff --git a/ProcessMaker/Mail/Transports/MicrosoftGraphTransport.php b/ProcessMaker/Mail/Transports/MicrosoftGraphTransport.php new file mode 100644 index 0000000000..59ff0d1b96 --- /dev/null +++ b/ProcessMaker/Mail/Transports/MicrosoftGraphTransport.php @@ -0,0 +1,53 @@ +client = $client ?? new Client([ + 'base_uri' => 'https://graph.microsoft.com/v1.0/', + ]); + } + + protected function doSend(SentMessage $message): void + { + $email = MessageConverter::toEmail($message->getOriginalMessage()); + $payload = MicrosoftGraphMessageConverter::toSendMailPayload($email); + $token = $this->tokenProvider->getAccessToken(); + + $response = $this->client->post('users/' . rawurlencode($this->senderEmail) . '/sendMail', [ + 'headers' => [ + 'Authorization' => 'Bearer ' . $token, + 'Content-Type' => 'application/json', + ], + 'json' => $payload, + 'http_errors' => false, + ]); + + if ($response->getStatusCode() >= 400) { + throw new TransportException( + 'Microsoft Graph send failed: ' . $response->getBody()->getContents() + ); + } + } + + public function __toString(): string + { + return 'microsoft-graph'; + } +} diff --git a/ProcessMaker/Managers/OauthMailManager.php b/ProcessMaker/Managers/OauthMailManager.php index 603b174970..58a7d08b12 100644 --- a/ProcessMaker/Managers/OauthMailManager.php +++ b/ProcessMaker/Managers/OauthMailManager.php @@ -7,6 +7,8 @@ use Google\Client as GoogleClient; use GuzzleHttp\Client; use Illuminate\Mail\MailManager; +use ProcessMaker\Mail\MicrosoftGraphTokenProvider; +use ProcessMaker\Mail\Transports\MicrosoftGraphTransport; use ProcessMaker\Models\EnvironmentVariable; use ProcessMaker\Models\Setting; use ProcessMaker\Packages\Connectors\Email\EmailConfig; @@ -77,6 +79,26 @@ protected function createSmtpTransport($config) return $transport; } + public function createTransport(array $config) + { + if ($this->app->config->get('mail.driver') === 'microsoft_graph') { + return $this->createMicrosoftGraphTransport($config); + } + + return parent::createTransport($config); + } + + protected function createMicrosoftGraphTransport(array $config) + { + return new MicrosoftGraphTransport( + new MicrosoftGraphTokenProvider( + $this->app->config->get('services.microsoft_graph', []), + $this->emailServerIndex ?? 0 + ), + $this->fromAddress + ); + } + public function checkForExpiredAccessToken() { switch ($this->authMethod) { diff --git a/config/mail.php b/config/mail.php index d78ea38d62..1b524c46c7 100644 --- a/config/mail.php +++ b/config/mail.php @@ -86,6 +86,10 @@ 'transport' => 'array', ], + 'microsoft_graph' => [ + 'transport' => 'microsoft_graph', + ], + 'failover' => [ 'transport' => 'failover', 'mailers' => [ diff --git a/resources/js/admin/settings/components/SettingsListing.vue b/resources/js/admin/settings/components/SettingsListing.vue index ecba515d51..0d489764b3 100644 --- a/resources/js/admin/settings/components/SettingsListing.vue +++ b/resources/js/admin/settings/components/SettingsListing.vue @@ -607,7 +607,7 @@ export default { filterEmailServerButtons(groupName, groupData, btn) { const authMethod = groupData.find(data => data.key.includes("EMAIL_CONNECTOR_MAIL_AUTH_METHOD")); const selectedAuthMethod = authMethod ? authMethod.ui.options[authMethod.config] : null; - const showAuthAccBtn = selectedAuthMethod && selectedAuthMethod !== 'standard' ? true : false; + const showAuthAccBtn = selectedAuthMethod && selectedAuthMethod !== 'standard'; if (groupName.includes('Email Server') && !showAuthAccBtn) { // Returns all 'top' position buttons except the '+ Mail Server' and 'Authorize Account' button for email server tabs