From 6d9e6f87f8610141ab6e51ad9659adf53ebaaf21 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Tue, 2 Apr 2019 17:39:04 +0200 Subject: [PATCH 01/10] Documented the Mime component --- components/mime.rst | 205 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 components/mime.rst diff --git a/components/mime.rst b/components/mime.rst new file mode 100644 index 00000000000..66252b4080b --- /dev/null +++ b/components/mime.rst @@ -0,0 +1,205 @@ +.. index:: + single: MIME + single: MIME Messages + single: Components; MIME + +The Mime Component +================== + + The MIME component allows manipulating the MIME messages used to send emails. + +Installation +------------ + +.. code-block:: terminal + + $ composer require symfony/mime + +Alternatively, you can clone the ``_ repository. + +.. include:: /components/require_autoload.rst.inc + +Introduction +------------ + +`MIME`_ (Multipurpose Internet Mail Extensions) is an Internet standard that +extends the original basic format of emails to support features like: + +* Headers and text contents using non-ASCII characters; +* Message bodies with multiple parts (e.g. HTML and plain text contents); +* Non-text attachments: audio, video, images, PDF, etc. + +The entire MIME standard is complex and huge, but Symfony abstracts all that +complexity to provide two ways of creating MIME messages: + +* A high-level API based on the :class:`Symfony\\Component\\Mime\\Email` class + to quickly create email messages with all the common features; +* A low-level API based on the :class:`Symfony\\Component\\Mime\\Message` class + to have an absolute control over every single part of the email message. + +Usage +----- + +Use the :class:`Symfony\\Component\\Mime\\Email` class and their *chainable* +methods to compose the entire email message:: + + use Symfony\Component\Mime\Email; + + $email = (new Email()) + ->from('fabien@symfony.com') + ->to('foo@example.com') + ->cc('bar@example.com') + ->bcc('baz@example.com') + ->replyTo('fabien@symfony.com') + ->priority(1) + ->subject('Important Notification') + ->text('Lorem ipsum...') + ->html('

Lorem ipsum

...

') + ; + +This purpose of this component is to create the email messages. Use the +:doc:`Mailer component ` to actually send them. In Symfony +applications, it's easier to use the :doc:`Mailer integration `. + +Email Addresses +--------------- + +All the methods that require email addresses (``from()``, ``to()``, etc.) accept +both strings and objects:: + + // ... + use Symfony\Component\Mime\Address; + use Symfony\Component\Mime\NamedAddress; + + $email = (new Email()) + // email address as a simple string + ->from('fabien@symfony.com') + + // email address as an object + ->from(new Address('fabien@symfony.com')) + + // email address as an object (email clients will display the name + // instead of the email address) + ->from(new NamedAddress('fabien@symfony.com', 'Fabien')) + + // ... + ; + +Multiple addresses are defined with the ``addXXX()`` methods:: + + $email = (new Email()) + ->to('foo@example.com') + ->addTo('bar@example.com') + ->addTo('baz@example.com') + + // ... + ; + +Alternatively, you can pass multiple addresses to each method:: + + $toAddresses = ['foo@example.com', new Address('bar@example.com')]; + + $email = (new Email()) + ->to(...$toAddresses) + ->cc('cc1@example.com', 'cc2@example.com') + + // ... + ; + +Message Contents +---------------- + +.. TODO + + +Embedding Images +---------------- + +If you want to display images inside your email contents, you must embed them +instead of adding them as attachments. First, use the ``embed()`` or +``embedFromPath()`` method to add an image from a file or resource:: + + $email = (new Email()) + // ... + // get the image contents from a PHP resource + ->embed(fopen('/path/to/images/logo.png', 'r'), 'logo') + // get the image contents from an existing file + ->embedFromPath('/path/to/images/signature.gif', 'footer-signature') + ; + +The second optional argument of both methods is the image name ("Content-ID" in +the MIME standard). Its value is an arbitrary string used later to reference the +images inside the HTML contents:: + + $email = (new Email()) + // ... + ->embed(fopen('/path/to/images/logo.png', 'r'), 'logo') + ->embedFromPath('/path/to/images/signature.gif', 'footer-signature') + // reference images using the syntax 'cid:' + "image embed name" + ->html(' ... ...') + ; + +File Attachments +---------------- + +Use the ``attachFromPath()`` method to attach files that exist in your file system:: + + $email = (new Email()) + // ... + ->attachFromPath('/path/to/documents/terms-of-use.pdf') + // optionally you can tell email clients to display a custom name for the file + ->attachFromPath('/path/to/documents/privacy.pdf', 'Privacy Policy') + // optionally you can provide an explicit MIME type (otherwise it's guessed) + ->attachFromPath('/path/to/documents/contract.doc', 'Contract', 'application/msword') + ; + +Alternatively you can use the ``attach()`` method to attach contents generated +with PHP resources:: + + $email = (new Email()) + // ... + ->attach(fopen('/path/to/documents/contract.doc', 'r')) + ; + +Message Headers +--------------- + +.. TODO + + +Raw Message Composition +----------------------- + +Before continuing, it's important to have a look at the low level structure of +an email message. Consider a message which includes some content as both text +and HTML, a single PNG image included in those contents and a PDF file attached +to it. The MIME standard structures that message as the following tree: + +.. code-block:: text + + multipart/mixed + ├── multipart/related + │   ├── multipart/alternative + │   │   ├── text/plain + │   │   └── text/html + │   └── image/png + └── application/pdf + +This is the purpose of each MIME message part: + +* ``multipart/alternative``: used when two or more parts are alternatives of the + same (or very similar) content. The preferred format must be added last. +* ``multipart/mixed``: used to send different content types in the same message, + such as when attaching files. +* ``multipart/related``: used to indicate that each message part is a component + of an aggregate whole. The most common usage is to display images embedded + in the message contents. + +.. TODO + +Learn More +---------- + +.. TODO: link to Twig integration, etc. + +.. _`MIME`: https://en.wikipedia.org/wiki/MIME From 958e4d8a6c53d927032fac082eff657608fc4f0d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Apr 2019 11:22:55 +0200 Subject: [PATCH 02/10] Added a placeholder article for the Mailer component --- components/mailer.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 components/mailer.rst diff --git a/components/mailer.rst b/components/mailer.rst new file mode 100644 index 00000000000..84b31446f09 --- /dev/null +++ b/components/mailer.rst @@ -0,0 +1,25 @@ +.. index:: + single: Mailer + single: Components; Mailer + +The Mailer Component +==================== + + The Mailer component helps sending emails. + +Installation +------------ + +.. code-block:: terminal + + $ composer require symfony/mailer + +Alternatively, you can clone the ``_ repository. + +.. include:: /components/require_autoload.rst.inc + +Usage +----- + +We're currently working on the documentation of this component that was just +added to Symfony. We'll publish it in a few days. From fe48193baeb4c6c4b4c0e36dee035ebb27e20424 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Apr 2019 12:10:54 +0200 Subject: [PATCH 03/10] Finished the first version of the docs --- components/mime.rst | 76 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/components/mime.rst b/components/mime.rst index 66252b4080b..2017afea53a 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -109,8 +109,23 @@ Alternatively, you can pass multiple addresses to each method:: Message Contents ---------------- -.. TODO +The text and HTML contents of the email messages can be strings (usually the +result of rendering some template) or PHP resources:: + $email = (new Email()) + // ... + // simple contents defined as a string + ->text('Lorem ipsum...') + ->html('

Lorem ipsum...

') + + // contents obtained from a PHP resource + ->text(fopen('/path/to/emails/user_signup.txt', 'r')) + ->html(fopen('/path/to/emails/user_signup.html', 'r')) + ; + +The :doc:`Mailer component ` provides deep integration with +the :doc:`Twig template engine ` to give you features such as CSS +style inlining and HTML/CSS frameworks to create complex HTML email messages. Embedding Images ---------------- @@ -161,14 +176,12 @@ with PHP resources:: ->attach(fopen('/path/to/documents/contract.doc', 'r')) ; -Message Headers ---------------- - -.. TODO - +Creating Raw Email Messages +--------------------------- -Raw Message Composition ------------------------ +This is useful for advanced applications that need absolute control over every +email part. It's not recommended for applications with regular email +requirements because it adds a considerable complexity for no real gain. Before continuing, it's important to have a look at the low level structure of an email message. Consider a message which includes some content as both text @@ -195,11 +208,50 @@ This is the purpose of each MIME message part: of an aggregate whole. The most common usage is to display images embedded in the message contents. -.. TODO +When using the low-level :class:`Symfony\\Component\\Mime\\Message` class to +create the email message, you must keep all the above in mind to define the +different parts of the email by hand:: + + use Symfony\Component\Mime\Header\Headers; + use Symfony\Component\Mime\Message; + use Symfony\Component\Mime\Part\Multipart\AlternativePart; + use Symfony\Component\Mime\Part\TextPart; + + $headers = (new Headers()) + ->addMailboxListHeader('From', ['fabien@symfony.com']) + ->addMailboxListHeader('To', ['foo@example.com']) + ->addTextHeader('Subject', 'Important Notification') + ; + + $textContent = new TextPart('Lorem ipsum...'); + $htmlContent = new TextPart('

Lorem ipsum

...

', 'html'); + $body = new AlternativePart($textContent, $htmlContent); + + $email = new Message($headers, $body); + +Embedding images and attaching files is possible by creating the appropriate +email multiparts:: + + // ... + use Symfony\Component\Mime\Part\DataPart; + use Symfony\Component\Mime\Part\Multipart\MixedPart; + use Symfony\Component\Mime\Part\Multipart\RelatedPart; + + // ... + $embeddedImage = new DataPart(fopen('/path/to/images/logo.png', 'r'), null, 'image/png'); + $imageCid = $embeddedImage->getContentId(); + + $attachedFile = new DataPart(fopen('/path/to/documents/terms-of-use.pdf', 'r'), null, 'application/pdf'); + + $textContent = new TextPart('Lorem ipsum...'); + $htmlContent = new TextPart(sprintf( + '

Lorem ipsum

...

', $imageCid + ), 'html'); + $bodyContent = new AlternativePart($textContent, $htmlContent); + $body = new RelatedPart($bodyContent, $embeddedImage); -Learn More ----------- + $messageParts = new MixedPart($body, $attachedFile); -.. TODO: link to Twig integration, etc. + $email = new Message($headers, $messageParts); .. _`MIME`: https://en.wikipedia.org/wiki/MIME From 69ff0e20a86b0aaae7906ab4b1eee8e44c5c44c0 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Apr 2019 12:53:53 +0200 Subject: [PATCH 04/10] Added a section about serializing messages --- components/mime.rst | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/components/mime.rst b/components/mime.rst index 2017afea53a..81037bf0fe8 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -254,4 +254,34 @@ email multiparts:: $email = new Message($headers, $messageParts); +Serializing Email Messages +-------------------------- + +Email messages created with either the ``Email`` or ``Message`` classes can be +serialized because they are simple data objects:: + + $email = (new Email()) + ->from('fabien@symfony.com') + // ... + ; + + $serializedEmail = serialize($email); + +If you want to store serialized email messages to recreate them later (e.g. to +include them in a message sent with the :doc:`Messenger component +`) use the ``toString()`` utility method and the +:class:`Symfony\\Component\\Mime\\RawMessage` class:: + + use Symfony\Component\Mime\RawMessage; + + // create the email and serialize it for later reuse + $email = (new Email()) + ->from('fabien@symfony.com') + // ... + ; + $serializedEmail = $email->toString(); + + // later, recreate the original message to actually send it + $message = new RawMessage($serializedEmail); + .. _`MIME`: https://en.wikipedia.org/wiki/MIME From cf6ff0b7d90b05e91cde88ee979b76de296b5ea2 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Apr 2019 17:16:26 +0200 Subject: [PATCH 05/10] Documented the Twig integration --- components/mime.rst | 321 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 318 insertions(+), 3 deletions(-) diff --git a/components/mime.rst b/components/mime.rst index 81037bf0fe8..04418147687 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -123,9 +123,11 @@ result of rendering some template) or PHP resources:: ->html(fopen('/path/to/emails/user_signup.html', 'r')) ; -The :doc:`Mailer component ` provides deep integration with -the :doc:`Twig template engine ` to give you features such as CSS -style inlining and HTML/CSS frameworks to create complex HTML email messages. +.. tip:: + + You can also use Twig templates to render the HTML and text contents. Read + the :ref:`mime-component-twig-integration` section later in this article to + learn more. Embedding Images ---------------- @@ -284,4 +286,317 @@ include them in a message sent with the :doc:`Messenger component // later, recreate the original message to actually send it $message = new RawMessage($serializedEmail); +.. _mime-component-twig-integration: + +Twig Integration +---------------- + +The Mime component integrates with the :doc:`Twig template engine ` +to provide advanced features such as CSS style inlining and support for HTML/CSS +frameworks to create complex HTML email messages. + +Rendering Email Contents with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you define the contents of your email in Twig templates, use the +:class:`Symfony\\Bridge\\Twig\\Mime\\TemplatedEmail` class. This class extends +from the :class:`Symfony\\Component\\Mime\\Email` class explained above and adds +some utility methods for Twig templates:: + + use Symfony\Bridge\Twig\Mime\TemplatedEmail; + + $email = (new TemplatedEmail()) + ->from('fabien@symfony.com') + ->fo('foo@example.com') + // ... + + // this method defines the path of the Twig template to render + ->template('messages/user/signup.html.twig') + + // this method defines the parameters (name => value) passed to templates + ->context([ + 'expiration_date' => new \DateTime('+7 days'), + 'username' => 'foo', + ]) + ; + +Once the email object has been created, use the +:class:`Symfony\\Bridge\\Twig\\Mime\\BodyRenderer` class to render the template +and update the email message contents with the results. Previously you need to +`set up Twig`_ to define where templates are located:: + + // ... + use Symfony\Bridge\Twig\Mime\BodyRenderer; + use Twig\Environment; + use Twig\Loader\FilesystemLoader; + + // when using the Mime component inside a full-stack Symfony application, you + // don't need to do this Twig setup. You only have to inject the 'twig' service + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($templateLoader); + + $renderer = new BodyRenderer($twig); + // this updates the $email object contents with the result of rendering + // the template defined earlier with the given context + $renderer->render($email); + +The last step is to create the Twig template used to render the contents: + +.. code-block:: html+twig + +

Welcome {{ username }}!

+ +

You signed up to our site using the following email:

+

{{ email.to }}

+ +

Click here to activate your account

+ +The Twig template has access to any of the parameters passed in the ``context`` +method of the ``TemplatedEmail`` class and also to a special variable called +``email`` which gives access to any of the email message properties. + +If you don't define the text content of the message, the ``BodyRenderer()`` +class generates it automatically converting the HTML contents into text. If you +prefer to define the text content yourself, use the ``text()`` method explained +in the previous sections or replace the ``template()`` method with these other +methods:: + + use Symfony\Bridge\Twig\Mime\TemplatedEmail; + + $email = (new TemplatedEmail()) + ->from('fabien@symfony.com') + ->fo('foo@example.com') + // ... + + ->textTemplate('messages/user/signup.txt.twig') + ->htmlTemplate('messages/user/signup.html.twig') + + // this method defines the parameters (name => value) passed to templates + ->context([ + // same as before... + ]) + ; + +Embedding Images in Emails with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of dealing with the ```` syntax explained in the +previous sections, when using Twig to render email contents you can refer to +image files as usual. First, define a Twig namespace called ``images`` to +simplify things later:: + + // ... + + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $templatedLoader->addPath(__DIR__.'/images', 'images'); + $twig = new Environment($templateLoader); + +Now, use the special ``email.image()`` Twig helper to embed the images inside +the email contents: + +.. code-block:: html+twig + + {# '@images/' refers to the Twig namespace defined earlier #} + + +

Welcome {{ username }}!

+ {# ... #} + +Attaching Files in Emails with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Similar to embedding images, the first step to attach files using Twig templates +is to define a dedicated Twig namespace (e.g. ``documents``) to simplify things +later:: + + // ... + + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $templatedLoader->addPath(__DIR__.'/documents', 'documents'); + $twig = new Environment($templateLoader); + +Now, use the special ``email.attach()`` Twig helper to attach files to the email +message within the Twig template: + +.. code-block:: html+twig + + {# '@documents/' refers to the Twig namespace defined earlier #} + {% do email.attach('@documents/terms-of-use.pdf') %} + +

Welcome {{ username }}!

+ {# ... #} + +.. note:: + + The `Twig do tag`_ is similar to the ``{{ ... }}`` syntax used to evaluate + expressions, but it doesn't generate any output. You can use it to set other + email properties: + + .. code-block:: html+twig + + {% do email.priority(3) %} + +

Welcome {{ username }}!

+ {# ... #} + +Inlining CSS Styles in Emails with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Designing the HTML contents of an email is very different from designing a +normal HTML page. For starters, most email clients only support a subset of all +CSS features. In addition, popular email clients such as Gmail don't support +defining styles inside ```` sections and you must **inline +all the CSS styles**. + +CSS inlining means that every HTML tag must define a ``style`` attribute with +all its CSS styles. This not only increases the email byte size significantly +but also makes it impossible to manage for complex emails. That's why Twig +provides a ``CssInlinerExtension`` that automates everything for you. First, +install the Twig extension in your application: + +.. code-block:: terminal + + $ composer require twig/cssinliner-extension + +Now, enable the extension (this is done automatically in Symfony applications):: + + // ... + use Twig\CssInliner\CssInlinerExtension; + + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($templateLoader); + $twig->addExtension(new CssInlinerExtension()); + +Finally, wrap the entire template contents with the ``inline_css`` filter +(to do so, it's better to use the `{% filter %} Twig tag`_ instead of the +traditional ``...|filter`` notation): + +.. code-block:: html+twig + + {% filter inline_css %} + + +

Welcome {{ username }}!

+ {# ... #} + {% endfilter %} + +You can also define some or all CSS style in external files and pass them as +arguments of the filter: + +.. code-block:: html+twig + + {# '@css/' refers to the Twig namespace defined earlier #} + {% filter inline_css('@css/mailing.css') %} + + +

Welcome {{ username }}!

+ {# ... #} + {% endfilter %} + +Rendering Markdown Contents in Emails with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Twig provides another extension called ``MarkdownExtension`` that lets you +define the email contents using the `Markdown syntax`_. In addition to the +extension, you must also install a Markdown conversion library (the extension is +compatible with all the popular libraries): + +.. code-block:: terminal + + $ composer require twig/markdown-extension + + # these libraries are compatible too: erusev/parsedown, michelf/php-markdown + $ composer require league/commonmark + +Now, enable the extension (this is done automatically in Symfony applications):: + + // ... + use Twig\Markdown\MarkdownExtension; + + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($templateLoader); + $twig->addExtension(new MarkdownExtension()); + +Finally, use the ``markdown`` filter to convert parts or the entire email +contents from Markdown to HTML: + +.. code-block:: html+twig + + {% filter markdown %} + Welcome {{ username }}! + ======================= + + You signed up to our site using the following email: + `{{ email.to }}` + + [Click here to activate your account]({{ url('...') }}) + {% endfilter %} + +Using the Inky Email Templating Language with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Creating beautifully designed emails that work on every email client is so +complex that there are HTML/CSS frameworks dedicated to that. One of the most +popular frameworks is called `Inky`_. It defines a syntax based on some simple +tags which are later transformed into the real HTML code sent to users: + +.. code-block:: html + + + + + This is a column. + + + +Twig provides integration with Inky via the ``InkyExtension``. First, install +the extension in your application: + +.. code-block:: terminal + + $ composer require twig/inky-extension + +Now, enable the extension (this is done automatically in Symfony applications):: + + // ... + use Twig\Inky\InkyExtension; + + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($templateLoader); + $twig->addExtension(new InkyExtension()); + +Finally, use the ``inky`` filter to convert parts or the entire email +contents from Inky to HTML: + +.. code-block:: html+twig + + {% filter inky %} + + + + +

Welcome {{ username }}!

+
+ + {# ... #} +
+
+ {% endfilter %} + +You can combine all filters to create complex email messages: + +.. code-block:: html+twig + + {% filter inky|inline_css(source('@zurb/stylesheets/main.css')) %} + {# ... #} + {% endfilter %} + .. _`MIME`: https://en.wikipedia.org/wiki/MIME +.. _`Twig do tag`: https://twig.symfony.com/do +.. _`{% filter %} Twig tag`: https://twig.symfony.com/doc/2.x/tags/filter.html +.. _`Markdown syntax`: https://commonmark.org/ +.. _`Inky`: https://foundation.zurb.com/emails.html From 426b864696f64ec2a1fbbaf9992cb67d55a56006 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Apr 2019 17:21:08 +0200 Subject: [PATCH 06/10] Minor tweak --- components/mime.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/mime.rst b/components/mime.rst index 04418147687..54a7246a982 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -57,7 +57,7 @@ methods to compose the entire email message:: ->html('

Lorem ipsum

...

') ; -This purpose of this component is to create the email messages. Use the +This only purpose of this component is to create the email messages. Use the :doc:`Mailer component ` to actually send them. In Symfony applications, it's easier to use the :doc:`Mailer integration `. From b21dfb7de3cc1bc959d20b09117475bdbc1d077e Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Wed, 3 Apr 2019 17:36:39 +0200 Subject: [PATCH 07/10] Removed all references to template() (to be removed soon) --- components/mime.rst | 75 +++++---------------------------------------- 1 file changed, 7 insertions(+), 68 deletions(-) diff --git a/components/mime.rst b/components/mime.rst index 54a7246a982..30dc8bd36bc 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -311,7 +311,7 @@ some utility methods for Twig templates:: // ... // this method defines the path of the Twig template to render - ->template('messages/user/signup.html.twig') + ->htmlTemplate('messages/user/signup.html.twig') // this method defines the parameters (name => value) passed to templates ->context([ @@ -320,10 +320,11 @@ some utility methods for Twig templates:: ]) ; -Once the email object has been created, use the +Once the email object has been created, you must `set up Twig`_ to define where +templates are located and then, use the :class:`Symfony\\Bridge\\Twig\\Mime\\BodyRenderer` class to render the template -and update the email message contents with the results. Previously you need to -`set up Twig`_ to define where templates are located:: +and update the email message contents with the results. All this is done +automatically when using the component inside a Symfony application:: // ... use Symfony\Bridge\Twig\Mime\BodyRenderer; @@ -358,8 +359,8 @@ method of the ``TemplatedEmail`` class and also to a special variable called If you don't define the text content of the message, the ``BodyRenderer()`` class generates it automatically converting the HTML contents into text. If you prefer to define the text content yourself, use the ``text()`` method explained -in the previous sections or replace the ``template()`` method with these other -methods:: +in the previous sections or the ``textTemplate()`` method provided by the +``TemplatedEmail`` class:: use Symfony\Bridge\Twig\Mime\TemplatedEmail; @@ -377,68 +378,6 @@ methods:: ]) ; -Embedding Images in Emails with Twig -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Instead of dealing with the ```` syntax explained in the -previous sections, when using Twig to render email contents you can refer to -image files as usual. First, define a Twig namespace called ``images`` to -simplify things later:: - - // ... - - $templateLoader = new FilesystemLoader(__DIR__.'/templates'); - $templatedLoader->addPath(__DIR__.'/images', 'images'); - $twig = new Environment($templateLoader); - -Now, use the special ``email.image()`` Twig helper to embed the images inside -the email contents: - -.. code-block:: html+twig - - {# '@images/' refers to the Twig namespace defined earlier #} - - -

Welcome {{ username }}!

- {# ... #} - -Attaching Files in Emails with Twig -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Similar to embedding images, the first step to attach files using Twig templates -is to define a dedicated Twig namespace (e.g. ``documents``) to simplify things -later:: - - // ... - - $templateLoader = new FilesystemLoader(__DIR__.'/templates'); - $templatedLoader->addPath(__DIR__.'/documents', 'documents'); - $twig = new Environment($templateLoader); - -Now, use the special ``email.attach()`` Twig helper to attach files to the email -message within the Twig template: - -.. code-block:: html+twig - - {# '@documents/' refers to the Twig namespace defined earlier #} - {% do email.attach('@documents/terms-of-use.pdf') %} - -

Welcome {{ username }}!

- {# ... #} - -.. note:: - - The `Twig do tag`_ is similar to the ``{{ ... }}`` syntax used to evaluate - expressions, but it doesn't generate any output. You can use it to set other - email properties: - - .. code-block:: html+twig - - {% do email.priority(3) %} - -

Welcome {{ username }}!

- {# ... #} - Inlining CSS Styles in Emails with Twig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 46cfc540d56e94580eddcfc1e6618a670a8817c5 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 4 Apr 2019 11:09:59 +0200 Subject: [PATCH 08/10] Fixes --- components/mime.rst | 98 +++++++++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 31 deletions(-) diff --git a/components/mime.rst b/components/mime.rst index 30dc8bd36bc..1071f5561cf 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -133,8 +133,12 @@ Embedding Images ---------------- If you want to display images inside your email contents, you must embed them -instead of adding them as attachments. First, use the ``embed()`` or -``embedFromPath()`` method to add an image from a file or resource:: +instead of adding them as attachments. When using Twig to render the email +contents, as explained :ref:`later in this article ` +the images are embedded automatically. Otherwise, you need to embed them manually. + +First, use the ``embed()`` or ``embedFromPath()`` method to add an image from a +file or resource:: $email = (new Email()) // ... @@ -183,12 +187,13 @@ Creating Raw Email Messages This is useful for advanced applications that need absolute control over every email part. It's not recommended for applications with regular email -requirements because it adds a considerable complexity for no real gain. +requirements because it adds complexity for no real gain. Before continuing, it's important to have a look at the low level structure of an email message. Consider a message which includes some content as both text -and HTML, a single PNG image included in those contents and a PDF file attached -to it. The MIME standard structures that message as the following tree: +and HTML, a single PNG image embedded in those contents and a PDF file attached +to it. The MIME standard allows structuring this message in different ways, but +the following tree is the one that works on most email clients: .. code-block:: text @@ -269,10 +274,11 @@ serialized because they are simple data objects:: $serializedEmail = serialize($email); -If you want to store serialized email messages to recreate them later (e.g. to -include them in a message sent with the :doc:`Messenger component -`) use the ``toString()`` utility method and the -:class:`Symfony\\Component\\Mime\\RawMessage` class:: +A common use case is to store serialized email messages, include them in a +message sent with the :doc:`Messenger component ` and +recreate them later when sending them. Use the +:class:`Symfony\\Component\\Mime\\RawMessage` class to recreate email messages +from their serialized contents:: use Symfony\Component\Mime\RawMessage; @@ -281,7 +287,7 @@ include them in a message sent with the :doc:`Messenger component ->from('fabien@symfony.com') // ... ; - $serializedEmail = $email->toString(); + $serializedEmail = serialize($email); // later, recreate the original message to actually send it $message = new RawMessage($serializedEmail); @@ -320,7 +326,7 @@ some utility methods for Twig templates:: ]) ; -Once the email object has been created, you must `set up Twig`_ to define where +Once the email object has been created, you must set up Twig to define where templates are located and then, use the :class:`Symfony\\Bridge\\Twig\\Mime\\BodyRenderer` class to render the template and update the email message contents with the results. All this is done @@ -333,8 +339,8 @@ automatically when using the component inside a Symfony application:: // when using the Mime component inside a full-stack Symfony application, you // don't need to do this Twig setup. You only have to inject the 'twig' service - $templateLoader = new FilesystemLoader(__DIR__.'/templates'); - $twig = new Environment($templateLoader); + $loader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($loader); $renderer = new BodyRenderer($twig); // this updates the $email object contents with the result of rendering @@ -354,13 +360,19 @@ The last step is to create the Twig template used to render the contents: The Twig template has access to any of the parameters passed in the ``context`` method of the ``TemplatedEmail`` class and also to a special variable called -``email`` which gives access to any of the email message properties. +``email``. This variable is an instance of the +:class:`Symfony\\Bridge\\Twig\\Mime\\WrappedTemplatedEmail` class which gives +access to some of the email message properties. -If you don't define the text content of the message, the ``BodyRenderer()`` -class generates it automatically converting the HTML contents into text. If you -prefer to define the text content yourself, use the ``text()`` method explained -in the previous sections or the ``textTemplate()`` method provided by the -``TemplatedEmail`` class:: +When the text content of the message is not defined explicitly, the +``BodyRenderer()`` class generates it automatically converting the HTML contents +into text. If you have `league/html-to-markdown`_ installed in your application, +it uses that to turn HTML into Markdown. Otherwise, it applies the +:phpfunction:`strip_tags` PHP function to the original HTML contents. + +If you prefer to define the text content yourself, use the ``text()`` method +explained in the previous sections or the ``textTemplate()`` method provided by +the ``TemplatedEmail`` class:: use Symfony\Bridge\Twig\Mime\TemplatedEmail; @@ -378,6 +390,33 @@ in the previous sections or the ``textTemplate()`` method provided by the ]) ; +.. _embedding-images-in-emails-with-twig: + +Embedding Images in Emails with Twig +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Instead of dealing with the ```` syntax explained in the +previous sections, when using Twig to render email contents you can refer to +image files as usual. First, define a Twig namespace called ``images`` to +simplify things later:: + + // ... + + $templateLoader = new FilesystemLoader(__DIR__.'/templates'); + $templatedLoader->addPath(__DIR__.'/images', 'images'); + $twig = new Environment($templateLoader); + +Now, use the special ``email.image()`` Twig helper to embed the images inside +the email contents: + +.. code-block:: html+twig + + {# '@images/' refers to the Twig namespace defined earlier #} + + +

Welcome {{ username }}!

+ {# ... #} + Inlining CSS Styles in Emails with Twig ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -402,13 +441,11 @@ Now, enable the extension (this is done automatically in Symfony applications):: // ... use Twig\CssInliner\CssInlinerExtension; - $templateLoader = new FilesystemLoader(__DIR__.'/templates'); - $twig = new Environment($templateLoader); + $loader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($loader); $twig->addExtension(new CssInlinerExtension()); -Finally, wrap the entire template contents with the ``inline_css`` filter -(to do so, it's better to use the `{% filter %} Twig tag`_ instead of the -traditional ``...|filter`` notation): +Finally, wrap the entire template contents with the ``inline_css`` filter: .. code-block:: html+twig @@ -421,7 +458,7 @@ traditional ``...|filter`` notation): {# ... #} {% endfilter %} -You can also define some or all CSS style in external files and pass them as +You can also define some or all CSS styles in external files and pass them as arguments of the filter: .. code-block:: html+twig @@ -456,8 +493,8 @@ Now, enable the extension (this is done automatically in Symfony applications):: // ... use Twig\Markdown\MarkdownExtension; - $templateLoader = new FilesystemLoader(__DIR__.'/templates'); - $twig = new Environment($templateLoader); + $loader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($loader); $twig->addExtension(new MarkdownExtension()); Finally, use the ``markdown`` filter to convert parts or the entire email @@ -504,8 +541,8 @@ Now, enable the extension (this is done automatically in Symfony applications):: // ... use Twig\Inky\InkyExtension; - $templateLoader = new FilesystemLoader(__DIR__.'/templates'); - $twig = new Environment($templateLoader); + $loader = new FilesystemLoader(__DIR__.'/templates'); + $twig = new Environment($loader); $twig->addExtension(new InkyExtension()); Finally, use the ``inky`` filter to convert parts or the entire email @@ -535,7 +572,6 @@ You can combine all filters to create complex email messages: {% endfilter %} .. _`MIME`: https://en.wikipedia.org/wiki/MIME -.. _`Twig do tag`: https://twig.symfony.com/do -.. _`{% filter %} Twig tag`: https://twig.symfony.com/doc/2.x/tags/filter.html +.. _`league/html-to-markdown`: https://github.com/thephpleague/html-to-markdown .. _`Markdown syntax`: https://commonmark.org/ .. _`Inky`: https://foundation.zurb.com/emails.html From ec9f85832a35baeecb2181e5b3b48d98cdf9c679 Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Thu, 4 Apr 2019 11:58:06 +0200 Subject: [PATCH 09/10] Added the MIME Types Utilities section --- components/mime.rst | 84 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 7 deletions(-) diff --git a/components/mime.rst b/components/mime.rst index 1071f5561cf..b667d3be62e 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -6,7 +6,8 @@ The Mime Component ================== - The MIME component allows manipulating the MIME messages used to send emails. + The MIME component allows manipulating the MIME messages used to send emails + and provides utilities related to MIME types. Installation ------------ @@ -282,15 +283,11 @@ from their serialized contents:: use Symfony\Component\Mime\RawMessage; - // create the email and serialize it for later reuse - $email = (new Email()) - ->from('fabien@symfony.com') - // ... - ; + // ... $serializedEmail = serialize($email); // later, recreate the original message to actually send it - $message = new RawMessage($serializedEmail); + $message = new RawMessage(unserialize($serializedEmail)); .. _mime-component-twig-integration: @@ -571,7 +568,80 @@ You can combine all filters to create complex email messages: {# ... #} {% endfilter %} +MIME Types Utilities +-------------------- + +Although MIME was designed mainly for creating emails, the content types (also +known as `MIME types`_ and "media types") defined by MIME standards are also of +importance in communication protocols outside of email, such as HTTP. That's +why this component also provides utilities to work with MIME types. + +The :class:`Symfony\\Component\\Mime\\MimeTypes` class transforms between +MIME types and file name extensions:: + + use Symfony\Component\Mime\MimeTypes; + + $mimeTypes = new MimeTypes(); + $exts = $mimeTypes->getExtensions('application/javascript'); + // $exts = ['js', 'jsm', 'mjs'] + $exts = $mimeTypes->getExtensions('image/jpeg'); + // $exts = ['jpeg', 'jpg', 'jpe'] + + $mimeTypes = $mimeTypes->getMimeTypes('js'); + // $mimeTypes = ['application/javascript', 'application/x-javascript', 'text/javascript'] + $mimeTypes = $mimeTypes->getMimeTypes('apk'); + // $mimeTypes = ['application/vnd.android.package-archive'] + +These methods return arrays with one or more elements. The element position +indicates its priority, so the first returned extension is the preferred one. + +Guessing the MIME Type +~~~~~~~~~~~~~~~~~~~~~~ + +Another useful utility allows to guess the MIME type of any given file:: + + use Symfony\Component\Mime\MimeTypes; + + $mimeTypes = new MimeTypes(); + $mimeType = $mimeTypes->guessMimeType('/some/path/to/image.gif'); + // Guessing is not based on the file name, so $mimeType will be 'image/gif' + // only if the given file is truly a GIF image + +Guessing the MIME type is a time-consuming process that requires inspecting +part of the file contents. Symfony applies multiple guessing mechanisms, one +of them based on the PHP `fileinfo extension`_. It's recommended to install +that extension to improve the guessing performance. + +Adding a MIME Type Guesser +.......................... + +You can register your own MIME type guesser by creating a class that implements +:class:`Symfony\\Component\\Mime\\MimeTypeGuesserInterface`:: + + namespace App; + + use Symfony\Component\Mime\MimeTypeGuesserInterface; + + class SomeMimeTypeGuesser implements MimeTypeGuesserInterface + { + public function isGuesserSupported(): bool + { + // return true when the guesser is supported (might depend on the OS for instance) + return true; + } + + public function guessMimeType(string $path): ?string + { + // inspect the contents of the file stored in $path to guess its + // type and return a valid MIME type ... or null if unknown + + return '...'; + } + } + .. _`MIME`: https://en.wikipedia.org/wiki/MIME .. _`league/html-to-markdown`: https://github.com/thephpleague/html-to-markdown .. _`Markdown syntax`: https://commonmark.org/ .. _`Inky`: https://foundation.zurb.com/emails.html +.. _`MIME types`: https://en.wikipedia.org/wiki/Media_type +.. _`fileinfo extension`: https://php.net/fileinfo From 8b3c0d703671d41319b253d4ea7976de2fcc723d Mon Sep 17 00:00:00 2001 From: Javier Eguiluz Date: Fri, 5 Apr 2019 09:42:28 +0200 Subject: [PATCH 10/10] Minor tweak in an example --- components/mime.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/mime.rst b/components/mime.rst index b667d3be62e..a2a49013f4a 100644 --- a/components/mime.rst +++ b/components/mime.rst @@ -381,9 +381,9 @@ the ``TemplatedEmail`` class:: ->textTemplate('messages/user/signup.txt.twig') ->htmlTemplate('messages/user/signup.html.twig') - // this method defines the parameters (name => value) passed to templates ->context([ - // same as before... + 'expiration_date' => new \DateTime('+7 days'), + 'username' => 'foo', ]) ;