FestinHegre/vendor/symfony/web-profiler-bundle/Resources/views/Collector/mailer.html.twig
2024-09-26 17:26:04 +02:00

512 lines
23 KiB
Twig

{% extends '@WebProfiler/Profiler/layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<style>
:root {
--mailer-email-table-wrapper-background: var(--gray-100);
--mailer-email-table-active-row-background: #dbeafe;
--mailer-email-table-active-row-color: var(--color-text);
}
.theme-dark {
--mailer-email-table-wrapper-background: var(--gray-900);
--mailer-email-table-active-row-background: var(--gray-300);
--mailer-email-table-active-row-color: var(--gray-800);
}
.mailer-email-summary-table-wrapper {
background: var(--mailer-email-table-wrapper-background);
border-bottom: 4px double var(--table-border-color);
border-radius: inherit;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin: 0 -9px 10px -9px;
padding-bottom: 10px;
transform: translateY(-9px);
max-height: 265px;
overflow-y: auto;
}
.mailer-email-summary-table,
.mailer-email-summary-table tr,
.mailer-email-summary-table td {
border: 0;
border-radius: inherit;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
box-shadow: none;
}
.mailer-email-summary-table th {
color: var(--color-muted);
font-size: 13px;
padding: 4px 10px;
}
.mailer-email-summary-table tr td,
.mailer-email-summary-table tr:last-of-type td {
border: solid var(--table-border-color);
border-width: 1px 0;
}
.mailer-email-summary-table-row {
margin: 5px 0;
}
.mailer-email-summary-table-row:hover {
cursor: pointer;
}
.mailer-email-summary-table-row.active {
background: var(--mailer-email-table-active-row-background);
color: var(--mailer-email-table-active-row-color);
}
.mailer-email-summary-table-row td {
font-family: var(--font-family-system);
font-size: inherit;
}
.mailer-email-details {
display: none;
}
.mailer-email-details.active {
display: block;
}
.mailer-transport-information {
border-bottom: 1px solid var(--form-input-border-color);
padding-bottom: 5px;
font-size: var(--font-size-body);
margin: 5px 0 10px 5px;
}
.mailer-transport-information .badge {
font-size: inherit;
font-weight: inherit;
}
.mailer-message-subject {
font-size: 21px;
font-weight: bold;
margin: 5px;
}
.mailer-message-headers {
margin-bottom: 10px;
}
.mailer-message-headers p {
font-size: var(--font-size-body);
margin: 2px 5px;
}
.mailer-message-header-secondary {
color: var(--color-muted);
}
.mailer-message-attachments-title {
align-items: center;
display: flex;
font-size: var(--font-size-body);
font-weight: 600;
margin-bottom: 10px;
}
.mailer-message-attachments-title svg {
color: var(--color-muted);
margin-right: 5px;
height: 18px;
width: 18px;
}
.mailer-message-attachments-title span {
font-weight: normal;
margin-left: 4px;
}
.mailer-message-attachments-list {
list-style: none;
margin: 0 0 5px 20px;
padding: 0;
}
.mailer-message-attachments-list li {
align-items: center;
display: flex;
}
.mailer-message-attachments-list li svg {
margin-right: 5px;
height: 18px;
width: 18px;
}
.mailer-message-attachments-list li a {
margin-left: 5px;
}
.mailer-email-body {
margin: 0;
padding: 6px 8px;
}
.mailer-empty-email-body {
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%23e5e5e5' stroke-width='4' stroke-dasharray='6%2c 14' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
border-radius: 6px;
color: var(--color-muted);
margin: 1em 0 0;
padding: .5em 1em;
}
.theme-dark .mailer-empty-email-body {
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='%23737373' stroke-width='4' stroke-dasharray='6%2c 14' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
}
.mailer-empty-email-body p {
font-size: var(--font-size-body);
margin: 0;
padding: 0.5em 0;
}
.mailer-message-download-raw {
align-items: center;
display: flex;
padding: 5px 0 0 5px;
}
.mailer-message-download-raw svg {
height: 18px;
width: 18px;
margin-right: 3px;
}
</style>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
window.addEventListener('DOMContentLoaded', () => {
new SymfonyProfilerMailerPanel();
});
class SymfonyProfilerMailerPanel {
constructor() {
this.#initializeEmailsTable();
}
#initializeEmailsTable() {
const emailRows = document.querySelectorAll('.mailer-email-summary-table-row');
emailRows.forEach((emailRow) => {
emailRow.addEventListener('click', () => {
emailRows.forEach((row) => row.classList.remove('active'));
emailRow.classList.add('active');
document.querySelectorAll('.mailer-email-details').forEach((emailDetails) => emailDetails.style.display = 'none');
document.querySelector(emailRow.getAttribute('data-target')).style.display = 'block';
});
});
}
}
</script>
{% endblock %}
{% block toolbar %}
{% set events = collector.events %}
{% if events.messages|length %}
{% set icon %}
{{ source('@WebProfiler/Icon/mailer.svg') }}
<span class="sf-toolbar-value">{{ events.messages|length }}</span>
{% endset %}
{% set text %}
<div class="sf-toolbar-info-piece">
<b>Queued messages</b>
<span class="sf-toolbar-status">{{ events.events|filter(e => e.isQueued())|length }}</span>
</div>
<div class="sf-toolbar-info-piece">
<b>Sent messages</b>
<span class="sf-toolbar-status">{{ events.events|filter(e => not e.isQueued())|length }}</span>
</div>
{% endset %}
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': profiler_url }) }}
{% endif %}
{% endblock %}
{% block menu %}
{% set events = collector.events %}
<span class="label {{ events.messages is empty ? 'disabled' }}">
<span class="icon">{{ source('@WebProfiler/Icon/mailer.svg') }}</span>
<strong>Emails</strong>
{% if events.messages|length > 0 %}
<span class="count">
<span>{{ events.messages|length }}</span>
</span>
{% endif %}
</span>
{% endblock %}
{% block panel %}
{% set events = collector.events %}
<h2>Emails</h2>
{% if not events.messages|length %}
<div class="empty empty-panel">
<p>No emails were sent.</p>
</div>
{% else %}
<div class="metrics">
<div class="metric-group">
<div class="metric">
<span class="value">{{ events.events|filter(e => e.isQueued())|length }}</span>
<span class="label">Queued</span>
</div>
<div class="metric">
<span class="value">{{ events.events|filter(e => not e.isQueued())|length }}</span>
<span class="label">Sent</span>
</div>
</div>
</div>
{% endif %}
{% if events.transports|length > 1 %}
{% for transport in events.transports %}
<h2><code>{{ transport }}</code> transport</h2>
{{ _self.render_transport_details(collector, transport) }}
{% endfor %}
{% elseif events.transports is not empty %}
{{ _self.render_transport_details(collector, events.transports|first, true) }}
{% endif %}
{% macro render_transport_details(collector, transport, show_transport_name = false) %}
<div class="card">
{% set num_emails = collector.events.events(transport)|length %}
{% if num_emails > 1 %}
<div class="mailer-email-summary-table-wrapper">
<table class="mailer-email-summary-table">
<thead>
<tr>
<th>#</th>
<th>Subject</th>
<th>To</th>
<th class="visually-hidden">Actions</th>
</tr>
</thead>
<tbody>
{% for event in collector.events.events(transport) %}
<tr class="mailer-email-summary-table-row {{ loop.first ? 'active' }}" data-target="#email-{{ loop.index }}">
<td>{{ loop.index }}</td>
<td>
{% if event.message.subject is defined %}
{{ event.message.getSubject() ?? '(No subject)' }}
{% elseif event.message.headers.has('subject') %}
{{ event.message.headers.get('subject').bodyAsString()|default('(No subject)') }}
{% else %}
(No subject)
{% endif %}
</td>
<td>
{% if event.message.to is defined %}
{{ event.message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
{% elseif event.message.headers.has('to') %}
{{ event.message.headers.get('to').bodyAsString()|default('(empty)') }}
{% else %}
(empty)
{% endif %}
</td>
<td class="visually-hidden"><button class="mailer-email-summary-table-row-button" data-target="#email-{{ loop.index }}">View email details</button></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% for event in collector.events.events(transport) %}
<div class="mailer-email-details {{ loop.first ? 'active' }}" id="email-{{ loop.index }}">
{{ _self.render_email_details(collector, transport, event.message, event.isQueued, show_transport_name) }}
</div>
{% endfor %}
{% else %}
{% set event = (collector.events.events(transport)|first) %}
{{ _self.render_email_details(collector, transport, event.message, event.isQueued, show_transport_name) }}
{% endif %}
</div>
{% endmacro %}
{% macro render_email_details(collector, transport, message, message_is_queued, show_transport_name = false) %}
{% if show_transport_name %}
<p class="mailer-transport-information">
<strong>Status:</strong> <span class="badge badge-{{ message_is_queued ? 'warning' : 'success' }}">{{ message_is_queued ? 'Queued' : 'Sent' }}</span>
&bull;
<strong>Transport:</strong> <code>{{ transport }}</code>
</p>
{% endif %}
{% if message.headers is not defined %}
{# render the raw message contents #}
<a class="mailer-message-download-raw" href="data:application/octet-stream;base64,{{ collector.base64Encode(message.toString()) }}" download="email.eml">
{{ source('@WebProfiler/Icon/download.svg') }}
Download as EML file
</a>
<pre class="prewrap" style="max-height: 600px; margin-left: 5px">{{ message.toString() }}</pre>
{% else %}
<div class="sf-tabs">
<div class="tab">
<h3 class="tab-title">Email contents</h3>
<div class="tab-content">
<div class="card-block">
<p class="mailer-message-subject">
{% if message.subject is defined %}
{{ message.getSubject() ?? '(No subject)' }}
{% elseif message.headers.has('subject') %}
{{ message.headers.get('subject').bodyAsString()|default('(No subject)') }}
{% else %}
(No subject)
{% endif %}
</p>
<div class="mailer-message-headers">
<p>
<strong>From:</strong>
{% if message.from is defined %}
{{ message.getFrom()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
{% elseif message.headers.has('from') %}
{{ message.headers.get('from').bodyAsString()|default('(empty)') }}
{% else %}
(empty)
{% endif %}
</p>
<p>
<strong>To:</strong>
{% if message.to is defined %}
{{ message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}
{% elseif message.headers.has('to') %}
{{ message.headers.get('to').bodyAsString()|default('(empty)') }}
{% else %}
(empty)
{% endif %}
</p>
{% for header in message.headers.all|filter(header => (header.name ?? '')|lower not in ['subject', 'from', 'to']) %}
<p class="mailer-message-header-secondary">{{ header.toString }}</p>
{% endfor %}
</div>
</div>
{% if message.attachments is defined and message.attachments %}
<div class="card-block">
{% set num_of_attachments = message.attachments|length %}
{% set total_attachments_size_in_bytes = message.attachments|reduce((total_size, attachment) => total_size + attachment.body|length, 0) %}
<p class="mailer-message-attachments-title">
{{ source('@WebProfiler/Icon/attachment.svg') }}
Attachments <span>({{ num_of_attachments }} file{{ num_of_attachments > 1 ? 's' }} / {{ _self.render_file_size_humanized(total_attachments_size_in_bytes) }})</span>
</p>
<ul class="mailer-message-attachments-list">
{% for attachment in message.attachments %}
<li>
{{ source('@WebProfiler/Icon/file.svg') }}
{% if attachment.filename|default %}
{{ attachment.filename }}
{% else %}
<em>(no filename)</em>
{% endif %}
({{ _self.render_file_size_humanized(attachment.body|length) }})
<a href="data:{{ attachment.contentType|default('application/octet-stream') }};base64,{{ collector.base64Encode(attachment.body) }}" download="{{ attachment.filename|default('attachment') }}">Download</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="card-block">
<div class="sf-tabs sf-tabs-sm">
{% if message.htmlBody is defined %}
{% set textBody = message.textBody %}
{% set htmlBody = message.htmlBody %}
<div class="tab {{ not textBody ? 'disabled' }} {{ textBody ? 'active' }}">
<h3 class="tab-title">Text content</h3>
<div class="tab-content">
{% if textBody %}
<pre class="mailer-email-body prewrap" style="max-height: 600px">
{%- if message.textCharset() %}
{{- textBody|convert_encoding('UTF-8', message.textCharset()) }}
{%- else %}
{{- textBody }}
{%- endif -%}
</pre>
{% else %}
<div class="mailer-empty-email-body">
<p>The text body is empty.</p>
</div>
{% endif %}
</div>
</div>
{% if htmlBody %}
<div class="tab">
<h3 class="tab-title">HTML preview</h3>
<div class="tab-content">
<pre class="prewrap" style="max-height: 600px"><iframe src="data:text/html;charset=utf-8;base64,{{ collector.base64Encode(htmlBody) }}" style="height: 80vh;width: 100%;"></iframe>
</pre>
</div>
</div>
{% endif %}
<div class="tab {{ not htmlBody ? 'disabled' }} {{ not textBody and htmlBody ? 'active' }}">
<h3 class="tab-title">HTML content</h3>
<div class="tab-content">
{% if htmlBody %}
<pre class="mailer-email-body prewrap" style="max-height: 600px">
{%- if message.htmlCharset() %}
{{- htmlBody|convert_encoding('UTF-8', message.htmlCharset()) }}
{%- else %}
{{- htmlBody }}
{%- endif -%}
</pre>
{% else %}
<div class="mailer-empty-email-body">
<p>The HTML body is empty.</p>
</div>
{% endif %}
</div>
</div>
{% else %}
{% set body = message.body ? message.body.toString() : null %}
<div class="tab {{ not body ? 'disabled' }} {{ body ? 'active' }}">
<h3 class="tab-title">Content</h3>
<div class="tab-content">
{% if body %}
<pre class="mailer-email-body prewrap" style="max-height: 600px">
{{- body }}
</pre>
{% else %}
<div class="mailer-empty-email-body">
<p>The body is empty.</p>
</div>
{% endif %}
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="tab">
<h3 class="tab-title">MIME parts</h3>
<div class="tab-content">
<pre class="prewrap" style="max-height: 600px; margin-left: 5px">{{ message.body().asDebugString() }}</pre>
</div>
</div>
<div class="tab">
<h3 class="tab-title">Raw Message</h3>
<div class="tab-content">
<a class="mailer-message-download-raw" href="data:application/octet-stream;base64,{{ collector.base64Encode(message.toString()) }}" download="email.eml">
{{ source('@WebProfiler/Icon/download.svg') }}
Download as EML file
</a>
<pre class="prewrap" style="max-height: 600px; margin-left: 5px">{{ message.toString() }}</pre>
</div>
</div>
</div>
{% endif %}
{% endmacro %}
{% macro render_file_size_humanized(bytes) %}
{%- if bytes < 1000 -%}
{{- bytes ~ ' bytes' -}}
{%- elseif bytes < 1000 ** 2 -%}
{{- (bytes / 1000)|number_format(2) ~ ' kB' -}}
{%- else -%}
{{- (bytes / 1000 ** 2)|number_format(2) ~ ' MB' -}}
{%- endif -%}
{% endmacro %}
{% endblock %}