Skip to content

Commit f143ee4

Browse files
committed
[Mailer] added support for the profiler
1 parent 5d5e04b commit f143ee4

File tree

8 files changed

+369
-1
lines changed

8 files changed

+369
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
553553
$loader->load('messenger_debug.xml');
554554
}
555555

556+
if (class_exists(Mailer::class)) {
557+
$loader->load('mailer_debug.xml');
558+
}
559+
556560
$container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
557561
$container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']);
558562

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="mailer.logger_message_listener" class="Symfony\Component\Mailer\EventListener\MessageLoggerListener">
9+
<tag name="kernel.event_subscriber"/>
10+
</service>
11+
12+
<service id="mailer.data_collector" class="Symfony\Component\Mailer\DataCollector\MessageDataCollector">
13+
<argument type="service" id="mailer.logger_message_listener" />
14+
<tag name="data_collector" template="@WebProfiler/Collector/mailer.html.twig" id="mailer" />
15+
</service>
16+
</services>
17+
</container>
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
2+
3+
{% block toolbar %}
4+
{% if collector.messages|length %}
5+
{% set icon %}
6+
{% include('@WebProfiler/Icon/mailer.svg') %}
7+
<span class="sf-toolbar-value">{{ collector.messages|length }}</span>
8+
{% endset %}
9+
10+
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': profiler_url }) }}
11+
{% endif %}
12+
{% endblock %}
13+
14+
{% block head %}
15+
{{ parent() }}
16+
<style type="text/css">
17+
/* utility classes */
18+
.m-t-0 { margin-top: 0 !important; }
19+
.m-t-10 { margin-top: 10px !important; }
20+
21+
/* basic grid */
22+
.row {
23+
display: flex;
24+
flex-wrap: wrap;
25+
margin-right: -15px;
26+
margin-left: -15px;
27+
}
28+
.col {
29+
flex-basis: 0;
30+
flex-grow: 1;
31+
max-width: 100%;
32+
position: relative;
33+
width: 100%;
34+
min-height: 1px;
35+
padding-right: 15px;
36+
padding-left: 15px;
37+
}
38+
.col-4 {
39+
flex: 0 0 33.333333%;
40+
max-width: 33.333333%;
41+
}
42+
43+
/* small tabs */
44+
.sf-tabs-sm .tab-navigation li {
45+
font-size: 14px;
46+
padding: .3em .5em;
47+
}
48+
</style>
49+
{% endblock %}
50+
51+
{% block menu %}
52+
<span class="label {{ collector.messages|length ? '' : 'disabled' }}">
53+
<span class="icon">{{ include('@WebProfiler/Icon/mailer.svg') }}</span>
54+
55+
<strong>Emails</strong>
56+
{% if collector.messages|length > 0 %}
57+
<span class="count">
58+
<span>{{ collector.messages|length }}</span>
59+
</span>
60+
{% endif %}
61+
</span>
62+
{% endblock %}
63+
64+
{% block panel %}
65+
<h2>Emails</h2>
66+
67+
{% if not collector.messages|length %}
68+
<div class="empty">
69+
<p>No emails were sent.</p>
70+
</div>
71+
{% endif %}
72+
73+
<div class="card-block">
74+
<div class="sf-tabs sf-tabs-sm">
75+
{% for message in collector.messages %}
76+
<div class="tab">
77+
<h3 class="tab-title">Email #{{ loop.index }}</h3>
78+
<div class="tab-content">
79+
<div class="card">
80+
{% if message.headers is not defined %}
81+
<div class="card-block">
82+
<pre class="prewrap" style="max-height: 600px">{{ message.toString() }}</pre>
83+
</div>
84+
{% else %}
85+
<div class="card-block">
86+
<span class="label">Subject</span>
87+
<h2 class="m-t-10">{{ message.headers.get('subject').bodyAsString() ?? '(empty)' }}</h2>
88+
</div>
89+
90+
<div class="card-block">
91+
<div class="row">
92+
<div class="col col-4">
93+
<span class="label">From</span>
94+
<pre class="prewrap">{{ (message.headers.get('from').bodyAsString() ?? '(empty)')|replace({'From:': ''}) }}</pre>
95+
96+
<span class="label">To</span>
97+
<pre class="prewrap">{{ (message.headers.get('to').bodyAsString() ?? '(empty)')|replace({'To:': ''}) }}</pre>
98+
</div>
99+
<div class="col">
100+
<span class="label">Headers</span>
101+
<pre class="prewrap">{% for header in message.headers.all|filter(header => (header.name ?? '') not in ['Subject', 'From', 'To']) %}
102+
{{- header.toString }}
103+
{%~ endfor %}</pre>
104+
</div>
105+
</div>
106+
</div>
107+
108+
<div class="card-block">
109+
<div class="sf-tabs sf-tabs-sm">
110+
<div class="tab">
111+
<h3 class="tab-title">HTML Content</h3>
112+
<div class="tab-content">
113+
<pre class="prewrap" style="max-height: 600px">
114+
{%- if message.htmlCharset() %}
115+
{{- message.htmlBody()|convert_encoding('UTF-8', message.htmlCharset()) }}
116+
{%- else %}
117+
{{- message.htmlBody() }}
118+
{%- endif -%}
119+
</pre>
120+
</div>
121+
</div>
122+
<div class="tab">
123+
<h3 class="tab-title">Text Content</h3>
124+
<div class="tab-content">
125+
<pre class="prewrap" style="max-height: 600px">
126+
{%- if message.textCharset() %}
127+
{{- message.textBody()|convert_encoding('UTF-8', message.textCharset()) }}
128+
{%- else %}
129+
{{- message.textBody() }}
130+
{%- endif -%}
131+
</pre>
132+
</div>
133+
</div>
134+
{% for attachment in message.attachments %}
135+
<div class="tab">
136+
<h3 class="tab-title">Attachment #{{ loop.index }}</h3>
137+
<div class="tab-content">
138+
<pre class="prewrap" style="max-height: 600px">{{ attachment.toString() }}</pre>
139+
</div>
140+
</div>
141+
{% endfor %}
142+
<div class="tab">
143+
<h3 class="tab-title">Raw</h3>
144+
<div class="tab-content">
145+
<pre class="prewrap" style="max-height: 600px">{{ message.toString() }}</pre>
146+
</div>
147+
</div>
148+
</div>
149+
</div>
150+
151+
{# Message just dump the body (or each part? -> be careful, it's nested and with attachments) or Email text/html/...? #}
152+
{#
153+
<div class="card-block">
154+
<div class="sf-tabs sf-tabs-sm">
155+
<div class="tab">
156+
<h3 class="tab-title">Raw content</h3>
157+
158+
<div class="tab-content">
159+
<pre class="prewrap" style="max-height: 600px">
160+
{%- if message.charset is defined and message.charset %}
161+
{{- message.body|convert_encoding('UTF-8', message.charset) }}
162+
{%- else %}
163+
{{- message.body }}
164+
{%- endif -%}
165+
</pre>
166+
</div>
167+
</div>
168+
169+
<div class="tab">
170+
<h3 class="tab-title">Rendered content</h3>
171+
172+
<div class="tab-content">
173+
<iframe class="full-width" style="min-height: 600px" src="data:{{ message.__contentType }};base64,{{ message.__base64EncodedBody }}"></iframe>
174+
</div>
175+
</div>
176+
</div>
177+
</div>
178+
179+
{% for messagePart in message.children|filter(messagePart => messagePart.contentType in ['text/plain', 'text/html']) %}
180+
<div class="card-block">
181+
<span class="label">Alternative part ({{ messagePart.contentType }})</span>
182+
<pre class="prewrap">
183+
{%- if messagePart.charset is defined and messagePart.charset %}
184+
{{- messagePart.body|convert_encoding('UTF-8', messagePart.charset) }}
185+
{%- else %}
186+
{{- messagePart.body }}
187+
{%- endif -%}
188+
</pre>
189+
</div>
190+
{% endfor %}
191+
192+
{% set attachments = collector.extractAttachments(message) %}
193+
{% if attachments %}
194+
<div class="card-block">
195+
<span class="label">
196+
{% if attachments|length > 1 %}
197+
{{ attachments|length }} Attachments
198+
{% else %}
199+
1 Attachment
200+
{% endif %}
201+
</span>
202+
203+
<ol>
204+
{% for attachment in attachments %}
205+
<li>
206+
Filename:
207+
{{ attachment.filename }}
208+
</li>
209+
{% endfor %}
210+
</ol>
211+
</div>
212+
{% endif %}
213+
#}
214+
{% endif %}
215+
</div>
216+
</div>
217+
</div>
218+
{% endfor %}
219+
</div>
220+
</div>
221+
{% endblock %}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\DataCollector;
13+
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
17+
use Symfony\Component\Mailer\EventListener\MessageLoggerListener;
18+
19+
/**
20+
* @author Fabien Potencier <fabien@symfony.com>
21+
*/
22+
class MessageDataCollector extends DataCollector
23+
{
24+
private $logger;
25+
26+
public function __construct(MessageLoggerListener $logger)
27+
{
28+
$this->logger = $logger;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function collect(Request $request, Response $response, \Exception $exception = null)
35+
{
36+
$this->data = [
37+
'messages' => $this->logger->getMessages(),
38+
];
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function reset()
45+
{
46+
$this->data = [];
47+
}
48+
49+
/**
50+
* @return Message[]
51+
*/
52+
public function getMessages(): array
53+
{
54+
return $this->data['messages'];
55+
}
56+
57+
/**
58+
* {@inheritdoc}
59+
*/
60+
public function getName()
61+
{
62+
return 'mailer';
63+
}
64+
}

src/Symfony/Component/Mailer/Event/MessageEvent.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Symfony\Component\Mime\RawMessage;
1717

1818
/**
19-
* Allows the transformation of a Message.
19+
* Allows the transformation of a Message and the SMTP Envelope before the email is sent.
2020
*
2121
* @author Fabien Potencier <fabien@symfony.com>
2222
*/
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\EventListener;
13+
14+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15+
use Symfony\Component\Mailer\Event\MessageEvent;
16+
use Symfony\Component\Mime\Message;
17+
18+
/**
19+
* Logs Messages.
20+
*
21+
* @author Fabien Potencier <fabien@symfony.com>
22+
*/
23+
class MessageLoggerListener implements EventSubscriberInterface
24+
{
25+
private $messages = [];
26+
27+
public function onMessage(MessageEvent $event): void
28+
{
29+
$this->messages[] = $event->getMessage();
30+
}
31+
32+
/**
33+
* @return Message[]
34+
*/
35+
public function getMessages(): array
36+
{
37+
return $this->messages;
38+
}
39+
40+
public static function getSubscribedEvents()
41+
{
42+
return [
43+
MessageEvent::class => ['onMessage', -255],
44+
];
45+
}
46+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy