Skip to content

Commit

Permalink
[Mime] added classes for generating MIME messages
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Mar 2, 2019
1 parent 91c5b14 commit ee787d1
Show file tree
Hide file tree
Showing 100 changed files with 8,201 additions and 17 deletions.
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -31,6 +31,7 @@
"symfony/contracts": "^1.0.2",
"symfony/polyfill-ctype": "~1.8",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-intl-idn": "^1.10",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php72": "~1.5",
"symfony/polyfill-php73": "^1.8"
Expand Down
100 changes: 100 additions & 0 deletions src/Symfony/Bridge/Twig/Mime/Renderer.php
@@ -0,0 +1,100 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Twig\Mime;

use League\HTMLToMarkdown\HtmlConverter;
use Twig\Environment;

/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 4.3
*/
final class Renderer
{
private $twig;
private $context;
private $converter;

public function __construct(Environment $twig, array $context = [])
{
$this->twig = $twig;
$this->context = $context;
if (class_exists(HtmlConverter::class)) {
$this->converter = new HtmlConverter([
'hard_break' => true,
'strip_tags' => true,
'remove_nodes' => 'head style',
]);
}
}

public function render(TemplatedEmail $email): TemplatedEmail
{
$email = clone $email;

$vars = array_merge($this->context, $email->getContext(), [
'email' => new WrappedTemplatedEmail($this->twig, $email),
]);

if ($template = $email->getTemplate()) {
$this->renderFull($email, $template, $vars);
}

if ($template = $email->getTextTemplate()) {
$email->text($this->twig->render($template, $vars));
}

if ($template = $email->getHtmlTemplate()) {
$email->html($this->twig->render($template, $vars));
}

// if text body is empty, compute one from the HTML body
if (!$email->getTextBody() && null !== $html = $email->getHtmlBody()) {
$email->text($this->convertHtmlToText(\is_resource($html) ? stream_get_contents($html) : $html));
}

return $email;
}

private function renderFull(TemplatedEmail $email, string $template, array $vars): void
{
$template = $this->twig->load($template);

if ($template->hasBlock('subject', $vars)) {
$email->subject($template->renderBlock('subject', $vars));
}

if ($template->hasBlock('text', $vars)) {
$email->text($template->renderBlock('text', $vars));
}

if ($template->hasBlock('html', $vars)) {
$email->html($template->renderBlock('html', $vars));
}

if ($template->hasBlock('config', $vars)) {
// we discard the output as we're only interested
// in the side effect of calling email methods
$template->renderBlock('config', $vars);
}
}

private function convertHtmlToText(string $html): string
{
if (null !== $this->converter) {
return $this->converter->convert($html);
}

return strip_tags($html);
}
}
87 changes: 87 additions & 0 deletions src/Symfony/Bridge/Twig/Mime/TemplatedEmail.php
@@ -0,0 +1,87 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Twig\Mime;

use Symfony\Component\Mime\Email;

/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 4.3
*/
class TemplatedEmail extends Email
{
private $template;
private $htmlTemplate;
private $textTemplate;
private $context = [];

/**
* @return $this
*/
public function template(?string $template)
{
$this->template = $template;

return $this;
}

/**
* @return $this
*/
public function textTemplate(?string $template)
{
$this->textTemplate = $template;

return $this;
}

/**
* @return $this
*/
public function htmlTemplate(?string $template)
{
$this->htmlTemplate = $template;

return $this;
}

public function getTemplate(): ?string
{
return $this->template;
}

public function getTextTemplate(): ?string
{
return $this->textTemplate;
}

public function getHtmlTemplate(): ?string
{
return $this->htmlTemplate;
}

/**
* @return $this
*/
public function context(array $context)
{
$this->context = $context;

return $this;
}

public function getContext(): array
{
return $this->context;
}
}
199 changes: 199 additions & 0 deletions src/Symfony/Bridge/Twig/Mime/WrappedTemplatedEmail.php
@@ -0,0 +1,199 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Bridge\Twig\Mime;

use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\NamedAddress;
use Twig\Environment;

/**
* @internal
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 4.3
*/
final class WrappedTemplatedEmail
{
private $twig;
private $message;

public function __construct(Environment $twig, TemplatedEmail $message)
{
$this->twig = $twig;
$this->message = $message;
}

public function toName(): string
{
$to = $this->message->getTo()[0];

return $to instanceof NamedAddress ? $to->getName() : '';
}

public function image(string $image, string $contentType = null): string
{
$file = $this->twig->getLoader()->getSourceContext($image);
if ($path = $file->getPath()) {
$this->message->embedFromPath($path, $image, $contentType);
} else {
$this->message->embed($file->getCode(), $image, $contentType);
}

return 'cid:'.$image;
}

public function attach(string $file, string $name = null, string $contentType = null): void
{
$file = $this->twig->getLoader()->getSourceContext($file);
if ($path = $file->getPath()) {
$this->message->attachFromPath($path, $name, $contentType);
} else {
$this->message->attach($file->getCode(), $name, $contentType);
}
}

/**
* @return $this
*/
public function setSubject(string $subject)
{
$this->message->subject($subject);

return $this;
}

public function getSubject(): ?string
{
return $this->message->getSubject();
}

/**
* @return $this
*/
public function setReturnPath(string $address)
{
$this->message->returnPath($address);

return $this;
}

public function getReturnPath(): string
{
return $this->message->getReturnPath();
}

/**
* @return $this
*/
public function addFrom(string $address, string $name = null)
{
$this->message->addFrom($name ? new NamedAddress($address, $name) : new Address($address));

return $this;
}

/**
* @return (Address|NamedAddress)[]
*/
public function getFrom(): array
{
return $this->message->getFrom();
}

/**
* @return $this
*/
public function addReplyTo(string $address)
{
$this->message->addReplyTo($address);

return $this;
}

/**
* @return Address[]
*/
public function getReplyTo(): array
{
return $this->message->getReplyTo();
}

/**
* @return $this
*/
public function addTo(string $address, string $name = null)
{
$this->message->addTo($name ? new NamedAddress($address, $name) : new Address($address));

return $this;
}

/**
* @return (Address|NamedAddress)[]
*/
public function getTo(): array
{
return $this->message->getTo();
}

/**
* @return $this
*/
public function addCc(string $address, string $name = null)
{
$this->message->addCc($name ? new NamedAddress($address, $name) : new Address($address));

return $this;
}

/**
* @return (Address|NamedAddress)[]
*/
public function getCc(): array
{
return $this->message->getCc();
}

/**
* @return $this
*/
public function addBcc(string $address, string $name = null)
{
$this->message->addBcc($name ? new NamedAddress($address, $name) : new Address($address));

return $this;
}

/**
* @return (Address|NamedAddress)[]
*/
public function getBcc(): array
{
return $this->message->getBcc();
}

/**
* @return $this
*/
public function setPriority(int $priority)
{
$this->message->setPriority($priority);

return $this;
}

public function getPriority(): int
{
return $this->message->getPriority();
}
}

0 comments on commit ee787d1

Please sign in to comment.