login consent app sql
This commit is contained in:
35
vendor/symfony/web-profiler-bundle/Resources/views/Collector/ajax.html.twig
vendored
Normal file
35
vendor/symfony/web-profiler-bundle/Resources/views/Collector/ajax.html.twig
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/ajax.svg') }}
|
||||
<span class="sf-toolbar-value sf-toolbar-ajax-request-counter">0</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<span class="sf-toolbar-header">
|
||||
<b class="sf-toolbar-ajax-info"></b>
|
||||
<b class="sf-toolbar-action">(<a class="sf-toolbar-ajax-clear" href="javascript:void(0);">Clear</a>)</b>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<table class="sf-toolbar-ajax-requests">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Profile</th>
|
||||
<th>Method</th>
|
||||
<th>Type</th>
|
||||
<th>Status</th>
|
||||
<th>URL</th>
|
||||
<th>Time</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="sf-toolbar-ajax-request-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: false }) }}
|
||||
{% endblock %}
|
153
vendor/symfony/web-profiler-bundle/Resources/views/Collector/cache.html.twig
vendored
Normal file
153
vendor/symfony/web-profiler-bundle/Resources/views/Collector/cache.html.twig
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.totals.calls > 0 %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/cache.svg') }}
|
||||
<span class="sf-toolbar-value">{{ collector.totals.calls }}</span>
|
||||
<span class="sf-toolbar-info-piece-additional-detail">
|
||||
<span class="sf-toolbar-label">in</span>
|
||||
<span class="sf-toolbar-value">{{ '%0.2f'|format(collector.totals.time * 1000) }}</span>
|
||||
<span class="sf-toolbar-label">ms</span>
|
||||
</span>
|
||||
{% endset %}
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Cache Calls</b>
|
||||
<span>{{ collector.totals.calls }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Total time</b>
|
||||
<span>{{ '%0.2f'|format(collector.totals.time * 1000) }} ms</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Cache hits</b>
|
||||
<span>{{ collector.totals.hits }} / {{ collector.totals.reads }}{% if collector.totals.hit_read_ratio is not null %} ({{ collector.totals.hit_read_ratio }}%){% endif %}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Cache writes</b>
|
||||
<span>{{ collector.totals.writes }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ collector.totals.calls == 0 ? 'disabled' }}">
|
||||
<span class="icon">
|
||||
{{ include('@WebProfiler/Icon/cache.svg') }}
|
||||
</span>
|
||||
<strong>Cache</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Cache</h2>
|
||||
|
||||
{% if collector.totals.calls == 0 %}
|
||||
<div class="empty">
|
||||
<p>No cache calls were made.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.totals.calls }}</span>
|
||||
<span class="label">Total calls</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ '%0.2f'|format(collector.totals.time * 1000) }} <span class="unit">ms</span></span>
|
||||
<span class="label">Total time</span>
|
||||
</div>
|
||||
<div class="metric-divider"></div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.totals.reads }}</span>
|
||||
<span class="label">Total reads</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.totals.writes }}</span>
|
||||
<span class="label">Total writes</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.totals.deletes }}</span>
|
||||
<span class="label">Total deletes</span>
|
||||
</div>
|
||||
<div class="metric-divider"></div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.totals.hits }}</span>
|
||||
<span class="label">Total hits</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.totals.misses }}</span>
|
||||
<span class="label">Total misses</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">
|
||||
{{ collector.totals.hit_read_ratio ?? 0 }} <span class="unit">%</span>
|
||||
</span>
|
||||
<span class="label">Hits/reads</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Pools</h2>
|
||||
<div class="sf-tabs">
|
||||
{% for name, calls in collector.calls %}
|
||||
<div class="tab {{ calls|length == 0 ? 'disabled' }}">
|
||||
<h3 class="tab-title">{{ name }} <span class="badge">{{ collector.statistics[name].calls }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% if calls|length == 0 %}
|
||||
<div class="empty">
|
||||
<p>No calls were made for {{ name }} pool.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<h4>Metrics</h4>
|
||||
<div class="metrics">
|
||||
{% for key, value in collector.statistics[name] %}
|
||||
<div class="metric">
|
||||
<span class="value">
|
||||
{% if key == 'time' %}
|
||||
{{ '%0.2f'|format(1000 * value) }} <span class="unit">ms</span>
|
||||
{% elseif key == 'hit_read_ratio' %}
|
||||
{{ value ?? 0 }} <span class="unit">%</span>
|
||||
{% else %}
|
||||
{{ value }}
|
||||
{% endif %}
|
||||
</span>
|
||||
<span class="label">{{ key == 'hit_read_ratio' ? 'Hits/reads' : key|capitalize }}</span>
|
||||
</div>
|
||||
{% if key == 'time' or key == 'deletes' %}
|
||||
<div class="metric-divider"></div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<h4>Calls</h4>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Time</th>
|
||||
<th>Call</th>
|
||||
<th>Hit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for call in calls %}
|
||||
<tr>
|
||||
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td>
|
||||
<td class="nowrap">{{ '%0.2f'|format((call.end - call.start) * 1000) }} ms</td>
|
||||
<td class="nowrap">{{ call.name }}()</td>
|
||||
<td>{{ profiler_dump(call.value.result, maxDepth=2) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
226
vendor/symfony/web-profiler-bundle/Resources/views/Collector/config.html.twig
vendored
Normal file
226
vendor/symfony/web-profiler-bundle/Resources/views/Collector/config.html.twig
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if 'unknown' == collector.symfonyState %}
|
||||
{% set block_status = '' %}
|
||||
{% set symfony_version_status = 'Unable to retrieve information about the Symfony version.' %}
|
||||
{% elseif 'eol' == collector.symfonyState %}
|
||||
{% set block_status = 'red' %}
|
||||
{% set symfony_version_status = 'This Symfony version will no longer receive security fixes.' %}
|
||||
{% elseif 'eom' == collector.symfonyState %}
|
||||
{% set block_status = 'yellow' %}
|
||||
{% set symfony_version_status = 'This Symfony version will only receive security fixes.' %}
|
||||
{% elseif 'dev' == collector.symfonyState %}
|
||||
{% set block_status = 'yellow' %}
|
||||
{% set symfony_version_status = 'This Symfony version is still in the development phase.' %}
|
||||
{% else %}
|
||||
{% set block_status = '' %}
|
||||
{% set symfony_version_status = '' %}
|
||||
{% endif %}
|
||||
|
||||
{% set icon %}
|
||||
<span class="sf-toolbar-label">
|
||||
{{ include('@WebProfiler/Icon/symfony.svg') }}
|
||||
</span>
|
||||
<span class="sf-toolbar-value">{{ collector.symfonyState is defined ? collector.symfonyversion : 'n/a' }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Profiler token</b>
|
||||
<span>
|
||||
{% if profiler_url %}
|
||||
<a href="{{ profiler_url }}">{{ collector.token }}</a>
|
||||
{% else %}
|
||||
{{ collector.token }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% if 'n/a' is not same as(collector.env) %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Environment</b>
|
||||
<span>{{ collector.env }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if 'n/a' is not same as(collector.debug) %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Debug</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.debug ? 'green' : 'red' }}">{{ collector.debug ? 'enabled' : 'disabled' }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece sf-toolbar-info-php">
|
||||
<b>PHP version</b>
|
||||
<span{% if collector.phpversionextra %} title="{{ collector.phpversion ~ collector.phpversionextra }}"{% endif %}>
|
||||
{{ collector.phpversion }}
|
||||
<a href="{{ path('_profiler_phpinfo') }}">View phpinfo()</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece sf-toolbar-info-php-ext">
|
||||
<b>PHP Extensions</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.hasxdebug ? 'green' : 'gray' }}">xdebug {{ collector.hasxdebug ? '✓' : '✗' }}</span>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.hasapcu ? 'green' : 'gray' }}">APCu {{ collector.hasapcu ? '✓' : '✗' }}</span>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.haszendopcache ? 'green' : 'red' }}">OPcache {{ collector.haszendopcache ? '✓' : '✗' }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>PHP SAPI</b>
|
||||
<span>{{ collector.sapiName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-group">
|
||||
{% if collector.symfonyversion is defined %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Resources</b>
|
||||
<span>
|
||||
<a href="https://symfony.com/doc/{{ collector.symfonyversion }}/index.html" rel="help">
|
||||
Read Symfony {{ collector.symfonyversion }} Docs
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Help</b>
|
||||
<span>
|
||||
<a href="https://symfony.com/support">
|
||||
Symfony Support Channels
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: true, name: 'config', status: block_status, additional_classes: 'sf-toolbar-block-right', block_attrs: 'title="' ~ symfony_version_status ~ '"' }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label label-status-{{ collector.symfonyState == 'eol' ? 'red' : collector.symfonyState in ['eom', 'dev'] ? 'yellow' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/config.svg') }}</span>
|
||||
<strong>Configuration</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Symfony Configuration</h2>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.symfonyversion }}</span>
|
||||
<span class="label">Symfony version</span>
|
||||
</div>
|
||||
|
||||
{% if 'n/a' is not same as(collector.env) %}
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.env }}</span>
|
||||
<span class="label">Environment</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if 'n/a' is not same as(collector.debug) %}
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.debug ? 'enabled' : 'disabled' }}</span>
|
||||
<span class="label">Debug</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% set symfony_status = { dev: 'Unstable Version', stable: 'Stable Version', eom: 'Maintenance Ended', eol: 'Version Expired' } %}
|
||||
{% set symfony_status_class = { dev: 'warning', stable: 'success', eom: 'warning', eol: 'error' } %}
|
||||
<table>
|
||||
<thead class="small">
|
||||
<tr>
|
||||
<th>Symfony Status</th>
|
||||
<th>Bugs {{ collector.symfonystate in ['eom', 'eol'] ? 'were' : 'are' }} fixed until</th>
|
||||
<th>Security issues {{ collector.symfonystate == 'eol' ? 'were' : 'are' }} fixed until</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="font-normal">
|
||||
<span class="label status-{{ symfony_status_class[collector.symfonystate] }}">{{ symfony_status[collector.symfonystate]|upper }}</span>
|
||||
{% if collector.symfonylts %}
|
||||
<span class="label status-success">Long-Term Support</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="font-normal">{{ collector.symfonyeom }}</td>
|
||||
<td class="font-normal">{{ collector.symfonyeol }}</td>
|
||||
<td class="font-normal">
|
||||
<a href="https://symfony.com/releases/{{ collector.symfonyminorversion }}#release-checker">View roadmap</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>PHP Configuration</h2>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.phpversion }}{% if collector.phpversionextra %} <span class="unit">{{ collector.phpversionextra }}</span>{% endif %}</span>
|
||||
<span class="label">PHP version</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.phparchitecture }} <span class="unit">bits</span></span>
|
||||
<span class="label">Architecture</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.phpintllocale }}</span>
|
||||
<span class="label">Intl locale</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.phptimezone }}</span>
|
||||
<span class="label">Timezone</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.haszendopcache ? 'yes' : 'no') ~ '.svg') }}</span>
|
||||
<span class="label">OPcache</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.hasapcu ? 'yes' : 'no-gray') ~ '.svg') }}</span>
|
||||
<span class="label">APCu</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.hasxdebug ? 'yes' : 'no-gray') ~ '.svg') }}</span>
|
||||
<span class="label">Xdebug</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<a href="{{ path('_profiler_phpinfo') }}">View full PHP configuration</a>
|
||||
</p>
|
||||
|
||||
{% if collector.bundles %}
|
||||
<h2>Enabled Bundles <small>({{ collector.bundles|length }})</small></h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="key">Name</th>
|
||||
<th>Class</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name in collector.bundles|keys|sort %}
|
||||
<tr>
|
||||
<th scope="row" class="font-normal">{{ name }}</th>
|
||||
<td class="font-normal">{{ profiler_dump(collector.bundles[name]) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endblock %}
|
119
vendor/symfony/web-profiler-bundle/Resources/views/Collector/events.html.twig
vendored
Normal file
119
vendor/symfony/web-profiler-bundle/Resources/views/Collector/events.html.twig
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/event.svg') }}</span>
|
||||
<strong>Events</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Event Dispatcher</h2>
|
||||
|
||||
{% if collector.calledlisteners is empty %}
|
||||
<div class="empty">
|
||||
<p>No events have been recorded. Check that debugging is enabled in the kernel.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="sf-tabs">
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Called Listeners <span class="badge">{{ collector.calledlisteners|length }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{{ helper.render_table(collector.calledlisteners) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Not Called Listeners <span class="badge">{{ collector.notcalledlisteners|length }}</span></h3>
|
||||
<div class="tab-content">
|
||||
{% if collector.notcalledlisteners is empty %}
|
||||
<div class="empty">
|
||||
<p>
|
||||
<strong>There are no uncalled listeners</strong>.
|
||||
</p>
|
||||
<p>
|
||||
All listeners were called for this request or an error occurred
|
||||
when trying to collect uncalled listeners (in which case check the
|
||||
logs to get more information).
|
||||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ helper.render_table(collector.notcalledlisteners) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Orphaned Events <span class="badge">{{ collector.orphanedEvents|length }}</span></h3>
|
||||
<div class="tab-content">
|
||||
{% if collector.orphanedEvents is empty %}
|
||||
<div class="empty">
|
||||
<p>
|
||||
<strong>There are no orphaned events</strong>.
|
||||
</p>
|
||||
<p>
|
||||
All dispatched events were handled or an error occurred
|
||||
when trying to collect orphaned events (in which case check the
|
||||
logs to get more information).
|
||||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Event</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for event in collector.orphanedEvents %}
|
||||
<tr>
|
||||
<td class="font-normal">{{ event }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% macro render_table(listeners) %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-right">Priority</th>
|
||||
<th>Listener</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{% set previous_event = (listeners|first).event %}
|
||||
{% for listener in listeners %}
|
||||
{% if loop.first or listener.event != previous_event %}
|
||||
{% if not loop.first %}
|
||||
</tbody>
|
||||
{% endif %}
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
<th colspan="2" class="colored font-normal">{{ listener.event }}</th>
|
||||
</tr>
|
||||
|
||||
{% set previous_event = listener.event %}
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<td class="text-right nowrap">{{ listener.priority|default('-') }}</td>
|
||||
<td class="font-normal">{{ profiler_dump(listener.stub) }}</td>
|
||||
</tr>
|
||||
|
||||
{% if loop.last %}
|
||||
</tbody>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endmacro %}
|
32
vendor/symfony/web-profiler-bundle/Resources/views/Collector/exception.css.twig
vendored
Normal file
32
vendor/symfony/web-profiler-bundle/Resources/views/Collector/exception.css.twig
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
.container {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container .container {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.exception-summary {
|
||||
background: var(--base-0);
|
||||
border: var(--border);
|
||||
box-shadow: 0 0 1px rgba(128, 128, 128, .2);
|
||||
margin: 1em 0;
|
||||
padding: 10px;
|
||||
}
|
||||
.exception-summary.exception-without-message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.exception-message {
|
||||
color: var(--color-error);
|
||||
}
|
||||
|
||||
.exception-metadata,
|
||||
.exception-illustration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.exception-message-wrapper .container {
|
||||
min-height: unset;
|
||||
}
|
37
vendor/symfony/web-profiler-bundle/Resources/views/Collector/exception.html.twig
vendored
Normal file
37
vendor/symfony/web-profiler-bundle/Resources/views/Collector/exception.html.twig
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block head %}
|
||||
{% if collector.hasexception %}
|
||||
<style>
|
||||
{{ render(controller('web_profiler.controller.exception_panel::stylesheet', { token: token })) }}
|
||||
{{ include('@WebProfiler/Collector/exception.css.twig') }}
|
||||
</style>
|
||||
{% endif %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ collector.hasexception ? 'label-status-error' : 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/exception.svg') }}</span>
|
||||
<strong>Exception</strong>
|
||||
{% if collector.hasexception %}
|
||||
<span class="count">
|
||||
<span>1</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Exceptions</h2>
|
||||
|
||||
{% if not collector.hasexception %}
|
||||
<div class="empty">
|
||||
<p>No exception was thrown and caught during the request.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="sf-reset">
|
||||
{{ render(controller('web_profiler.controller.exception_panel::body', { token: token })) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
730
vendor/symfony/web-profiler-bundle/Resources/views/Collector/form.html.twig
vendored
Normal file
730
vendor/symfony/web-profiler-bundle/Resources/views/Collector/form.html.twig
vendored
Normal file
@ -0,0 +1,730 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% from _self import form_tree_entry, form_tree_details %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.data.nb_errors > 0 or collector.data.forms|length %}
|
||||
{% set status_color = collector.data.nb_errors ? 'red' %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/form.svg') }}
|
||||
<span class="sf-toolbar-value">
|
||||
{{ collector.data.nb_errors ?: collector.data.forms|length }}
|
||||
</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Number of forms</b>
|
||||
<span class="sf-toolbar-status">{{ collector.data.forms|length }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Number of errors</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.data.nb_errors > 0 ? 'red' }}">{{ collector.data.nb_errors }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label label-status-{{ collector.data.nb_errors ? 'error' }} {{ collector.data.forms is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/form.svg') }}</span>
|
||||
<strong>Forms</strong>
|
||||
{% if collector.data.nb_errors > 0 %}
|
||||
<span class="count">
|
||||
<span>{{ collector.data.nb_errors }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ parent() }}
|
||||
|
||||
<style>
|
||||
#tree-menu {
|
||||
float: left;
|
||||
padding-right: 10px;
|
||||
width: 230px;
|
||||
}
|
||||
#tree-menu ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
#tree-menu li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#tree-menu .empty {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#tree-details-container {
|
||||
border-left: 1px solid #DDD;
|
||||
margin-left: 250px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
.tree-details {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.tree-details h3 {
|
||||
font-size: 18px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
display: inline-block;
|
||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDgwx4LcKwAAAABVQTFRFAAAA////////////////ZmZm////bvjBwAAAAAV0Uk5TABZwsuCVEUjgAAAAAWJLR0QF+G/pxwAAAE1JREFUGNNjSHMSYGBgUEljSGYAAzMGBwiDhUEBwmBiEIAwGBmwgTQgQGWgA7h2uIFwK+CWwp1BpHvYEqDuATEYkBlY3IOmBq6dCPcAAIT5Eg2IksjQAAAAAElFTkSuQmCC") no-repeat top left #5eb5e0;
|
||||
}
|
||||
.closed .toggle-icon, .closed.toggle-icon {
|
||||
background-position: bottom left;
|
||||
}
|
||||
.toggle-icon.empty {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAZgBmAGYHukptAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDhIf6CA40AAAAFRJREFUOMvtk7ENACEMA61vfx767MROWfO+AdGBHlNyTZrYUZRYDBII4NWE1pNdpFarfgLUbpDaBEgBYRiEVjsvDLa1l6O4Z3wkFWN+OfLKdpisOH/TlICzukmUJwAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.tree .tree-inner {
|
||||
cursor: pointer;
|
||||
padding: 5px 7px 5px 22px;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
.tree .toggle-button {
|
||||
/* provide a bigger clickable area than just 10x10px */
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: -18px;
|
||||
}
|
||||
.tree .toggle-icon {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
/* position the icon in the center of the clickable area */
|
||||
margin-left: 3px;
|
||||
margin-top: 3px;
|
||||
background-size: 10px 20px;
|
||||
background-color: #AAA;
|
||||
}
|
||||
.tree .toggle-icon.empty {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -5px;
|
||||
margin-left: -15px;
|
||||
background-size: 10px 10px;
|
||||
}
|
||||
.tree ul ul .tree-inner {
|
||||
padding-left: 37px;
|
||||
}
|
||||
.tree ul ul ul .tree-inner {
|
||||
padding-left: 52px;
|
||||
}
|
||||
.tree ul ul ul ul .tree-inner {
|
||||
padding-left: 67px;
|
||||
}
|
||||
.tree ul ul ul ul ul .tree-inner {
|
||||
padding-left: 82px;
|
||||
}
|
||||
.tree .tree-inner:hover {
|
||||
background: #dfdfdf;
|
||||
}
|
||||
.tree .tree-inner:hover span:not(.has-error) {
|
||||
color: var(--base-0);
|
||||
}
|
||||
.tree .tree-inner.active, .tree .tree-inner.active:hover {
|
||||
background: var(--tree-active-background);
|
||||
font-weight: bold;
|
||||
}
|
||||
.tree .tree-inner.active .toggle-icon, .tree .tree-inner:hover .toggle-icon, .tree .tree-inner.active:hover .toggle-icon {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDhEYXWn+sAAAABhQTFRFAAAA39/f39/f39/f39/fZmZm39/f////gc3YPwAAAAV0Uk5TAAtAc6ZeVyCYAAAAAWJLR0QF+G/pxwAAAE1JREFUGNNjSHMSYGBgUEljSGYAAzMGBwiDhUEBwmBiEIAwGBmwgXIgQGWgA7h2uIFwK+CWwp1BpHvYC6DuATEYkBlY3IOmBq6dCPcAADqLE4MnBi/fAAAAAElFTkSuQmCC");
|
||||
background-color: #999;
|
||||
}
|
||||
.tree .tree-inner.active .toggle-icon.empty, .tree .tree-inner:hover .toggle-icon.empty, .tree .tree-inner.active:hover .toggle-icon.empty {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QweDhoucSey4gAAABVQTFRFAAAA39/f39/f39/f39/fZmZm39/fD5Dx2AAAAAV0Uk5TAAtAc6ZeVyCYAAAAAWJLR0QF+G/pxwAAADJJREFUCNdjSHMSYGBgUEljSGYAAzMGBwiDhUEBwmBiEIAwGBnIA3DtcAPhVsAthTkDAFOfBKW9C1iqAAAAAElFTkSuQmCC");
|
||||
}
|
||||
.tree-details .toggle-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
/* vertically center the button */
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -9px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.badge-error {
|
||||
float: right;
|
||||
background: var(--background-error);
|
||||
color: #FFF;
|
||||
padding: 1px 4px;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.has-error {
|
||||
color: var(--color-error);
|
||||
}
|
||||
.errors h3 {
|
||||
color: var(--color-error);
|
||||
}
|
||||
.errors th {
|
||||
background: var(--background-error);
|
||||
color: #FFF;
|
||||
}
|
||||
.errors .toggle-icon {
|
||||
background-color: var(--background-error);
|
||||
}
|
||||
h3 a, h3 a:hover, h3 a:focus {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
h2 + h3.form-data-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
h3.form-data-type + h3 {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.theme-dark .toggle-icon {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAVUExURUdwTH+Ag0lNUZiYmGRmbP///zU5P2n9VV4AAAAFdFJOUwCv+yror0g1sQAAAE1JREFUGNNjSFM0YGBgEEpjSGEAAzcGBQiDiUEAwmBkMIAwmBmwgVAgQGWgA7h2uIFwK+CWwp1BpHtYA6DuATEYkBlY3IOmBq6dCPcAAKMtEEs3tfChAAAAAElFTkSuQmCC');
|
||||
}
|
||||
.theme-dark .toggle-icon.empty {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAASUExURUdwTDI3OzQ5PS4uLjU3PzU5P4keoyIAAAAFdFJOUwBApgtzrnKGEwAAADJJREFUCNdjCFU0YGBgEAplCGEAA1cGBQiDiUEAwmBkMIAwmBnIA3DtcAPhVsAthTkDACsZBBmrTTSxAAAAAElFTkSuQmCC');
|
||||
}
|
||||
.theme-dark .tree .tree-inner.active .toggle-icon, .theme-dark .tree .tree-inner:hover .toggle-icon, .theme-dark .tree .tree-inner.active:hover .toggle-icon {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAgBAMAAADpp+X/AAAAD1BMVEVHcEx/gIOYmJiZmZn///+IJ2wIAAAAA3RSTlMAryoIUq0uAAAAUElEQVQY02NgYFQ2NjYWYGBgMAYDBgZmCMOAQRjCMGRQhjCMoEqAipAYLkCAykBXA9cONxBuBdxShDOIc4+JM9Q9IIYxMgOLe9DUwLUT4R4AznguG0qfEa0AAAAASUVORK5CYII=');
|
||||
background-color: transparent;
|
||||
}
|
||||
.theme-dark .tree .tree-inner.active .toggle-icon.empty, .theme-dark .tree .tree-inner:hover .toggle-icon.empty, .theme-dark .tree .tree-inner.active:hover .toggle-icon.empty {
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEVHcEwyNzuqqqrd9nIgAAAAAnRSTlMAQABPjKgAAAArSURBVAjXY2BctcqBgWvVqgUMWqtWrWDIWrVqJcMqICCGACsGawMbADIKANflJYEoGMqtAAAAAElFTkSuQmCC');
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Forms</h2>
|
||||
|
||||
{% if collector.data.forms|length %}
|
||||
<div id="tree-menu" class="tree">
|
||||
<ul>
|
||||
{% for formName, formData in collector.data.forms %}
|
||||
{{ form_tree_entry(formName, formData, true) }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="tree-details-container">
|
||||
{% for formName, formData in collector.data.forms %}
|
||||
{{ form_tree_details(formName, formData, collector.data.forms_by_hash, loop.first) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<p>No forms were submitted for this request.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function Toggler(storage) {
|
||||
"use strict";
|
||||
|
||||
var STORAGE_KEY = 'sf_toggle_data',
|
||||
|
||||
states = {},
|
||||
|
||||
isCollapsed = function (button) {
|
||||
return Sfjs.hasClass(button, 'closed');
|
||||
},
|
||||
|
||||
isExpanded = function (button) {
|
||||
return !isCollapsed(button);
|
||||
},
|
||||
|
||||
expand = function (button) {
|
||||
var targetId = button.dataset.toggleTargetId,
|
||||
target = document.getElementById(targetId);
|
||||
|
||||
if (!target) {
|
||||
throw "Toggle target " + targetId + " does not exist";
|
||||
}
|
||||
|
||||
if (isCollapsed(button)) {
|
||||
Sfjs.removeClass(button, 'closed');
|
||||
Sfjs.removeClass(target, 'hidden');
|
||||
|
||||
states[targetId] = 1;
|
||||
storage.setItem(STORAGE_KEY, states);
|
||||
}
|
||||
},
|
||||
|
||||
collapse = function (button) {
|
||||
var targetId = button.dataset.toggleTargetId,
|
||||
target = document.getElementById(targetId);
|
||||
|
||||
if (!target) {
|
||||
throw "Toggle target " + targetId + " does not exist";
|
||||
}
|
||||
|
||||
if (isExpanded(button)) {
|
||||
Sfjs.addClass(button, 'closed');
|
||||
Sfjs.addClass(target, 'hidden');
|
||||
|
||||
states[targetId] = 0;
|
||||
storage.setItem(STORAGE_KEY, states);
|
||||
}
|
||||
},
|
||||
|
||||
toggle = function (button) {
|
||||
if (Sfjs.hasClass(button, 'closed')) {
|
||||
expand(button);
|
||||
} else {
|
||||
collapse(button);
|
||||
}
|
||||
},
|
||||
|
||||
initButtons = function (buttons) {
|
||||
states = storage.getItem(STORAGE_KEY, {});
|
||||
|
||||
// must be an object, not an array or anything else
|
||||
// `typeof` returns "object" also for arrays, so the following
|
||||
// check must be done
|
||||
// see http://stackoverflow.com/questions/4775722/check-if-object-is-array
|
||||
if ('[object Object]' !== Object.prototype.toString.call(states)) {
|
||||
states = {};
|
||||
}
|
||||
|
||||
for (var i = 0, l = buttons.length; i < l; ++i) {
|
||||
var targetId = buttons[i].dataset.toggleTargetId,
|
||||
target = document.getElementById(targetId);
|
||||
|
||||
if (!target) {
|
||||
throw "Toggle target " + targetId + " does not exist";
|
||||
}
|
||||
|
||||
// correct the initial state of the button
|
||||
if (Sfjs.hasClass(target, 'hidden')) {
|
||||
Sfjs.addClass(buttons[i], 'closed');
|
||||
}
|
||||
|
||||
// attach listener for expanding/collapsing the target
|
||||
clickHandler(buttons[i], toggle);
|
||||
|
||||
if (states.hasOwnProperty(targetId)) {
|
||||
// open or collapse based on stored data
|
||||
if (0 === states[targetId]) {
|
||||
collapse(buttons[i]);
|
||||
} else {
|
||||
expand(buttons[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
initButtons: initButtons,
|
||||
|
||||
toggle: toggle,
|
||||
|
||||
isExpanded: isExpanded,
|
||||
|
||||
isCollapsed: isCollapsed,
|
||||
|
||||
expand: expand,
|
||||
|
||||
collapse: collapse
|
||||
};
|
||||
}
|
||||
|
||||
function JsonStorage(storage) {
|
||||
var setItem = function (key, data) {
|
||||
storage.setItem(key, JSON.stringify(data));
|
||||
},
|
||||
|
||||
getItem = function (key, defaultValue) {
|
||||
var data = storage.getItem(key);
|
||||
|
||||
if (null !== data) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch(e) {
|
||||
}
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
return {
|
||||
setItem: setItem,
|
||||
|
||||
getItem: getItem
|
||||
};
|
||||
}
|
||||
|
||||
function TabView() {
|
||||
"use strict";
|
||||
|
||||
var activeTab = null,
|
||||
|
||||
activeTarget = null,
|
||||
|
||||
select = function (tab) {
|
||||
var targetId = tab.dataset.tabTargetId,
|
||||
target = document.getElementById(targetId);
|
||||
|
||||
if (!target) {
|
||||
throw "Tab target " + targetId + " does not exist";
|
||||
}
|
||||
|
||||
if (activeTab) {
|
||||
Sfjs.removeClass(activeTab, 'active');
|
||||
}
|
||||
|
||||
if (activeTarget) {
|
||||
Sfjs.addClass(activeTarget, 'hidden');
|
||||
}
|
||||
|
||||
Sfjs.addClass(tab, 'active');
|
||||
Sfjs.removeClass(target, 'hidden');
|
||||
|
||||
activeTab = tab;
|
||||
activeTarget = target;
|
||||
},
|
||||
|
||||
initTabs = function (tabs) {
|
||||
for (var i = 0, l = tabs.length; i < l; ++i) {
|
||||
var targetId = tabs[i].dataset.tabTargetId,
|
||||
target = document.getElementById(targetId);
|
||||
|
||||
if (!target) {
|
||||
throw "Tab target " + targetId + " does not exist";
|
||||
}
|
||||
|
||||
clickHandler(tabs[i], select);
|
||||
|
||||
Sfjs.addClass(target, 'hidden');
|
||||
}
|
||||
|
||||
if (tabs.length > 0) {
|
||||
select(tabs[0]);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
initTabs: initTabs,
|
||||
|
||||
select: select
|
||||
};
|
||||
}
|
||||
|
||||
var tabTarget = new TabView(),
|
||||
toggler = new Toggler(new JsonStorage(sessionStorage)),
|
||||
clickHandler = function (element, callback) {
|
||||
Sfjs.addEventListener(element, 'click', function (e) {
|
||||
if (!e) {
|
||||
e = window.event;
|
||||
}
|
||||
|
||||
callback(this);
|
||||
|
||||
if (e.preventDefault) {
|
||||
e.preventDefault();
|
||||
} else {
|
||||
e.returnValue = false;
|
||||
}
|
||||
|
||||
e.stopPropagation();
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
tabTarget.initTabs(document.querySelectorAll('.tree .tree-inner'));
|
||||
toggler.initButtons(document.querySelectorAll('a.toggle-button'));
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% macro form_tree_entry(name, data, is_root) %}
|
||||
{% import _self as tree %}
|
||||
{% set has_error = data.errors is defined and data.errors|length > 0 %}
|
||||
<li>
|
||||
<div class="tree-inner" data-tab-target-id="{{ data.id }}-details">
|
||||
{% if has_error %}
|
||||
<div class="badge-error">{{ data.errors|length }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if data.children is not empty %}
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-children" href="#"><span class="toggle-icon"></span></a>
|
||||
{% else %}
|
||||
<div class="toggle-icon empty"></div>
|
||||
{% endif %}
|
||||
|
||||
<span {% if has_error or data.has_children_error|default(false) %}class="has-error"{% endif %}>
|
||||
{{ name|default('(no name)') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% if data.children is not empty %}
|
||||
<ul id="{{ data.id }}-children" {% if not is_root and not data.has_children_error|default(false) %}class="hidden"{% endif %}>
|
||||
{% for childName, childData in data.children %}
|
||||
{{ tree.form_tree_entry(childName, childData, false) }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro form_tree_details(name, data, forms_by_hash, show) %}
|
||||
{% import _self as tree %}
|
||||
<div class="tree-details{% if not show|default(false) %} hidden{% endif %}" {% if data.id is defined %}id="{{ data.id }}-details"{% endif %}>
|
||||
<h2>{{ name|default('(no name)') }}</h2>
|
||||
{% if data.type_class is defined %}
|
||||
<h3 class="dump-inline form-data-type">{{ profiler_dump(data.type_class) }}</h3>
|
||||
{% endif %}
|
||||
|
||||
{% if data.errors is defined and data.errors|length > 0 %}
|
||||
<div class="errors">
|
||||
<h3>
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-errors" href="#">
|
||||
Errors <span class="toggle-icon"></span>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<table id="{{ data.id }}-errors">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Message</th>
|
||||
<th>Origin</th>
|
||||
<th>Cause</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for error in data.errors %}
|
||||
<tr>
|
||||
<td>{{ error.message }}</td>
|
||||
<td>
|
||||
{% if error.origin is empty %}
|
||||
<em>This form.</em>
|
||||
{% elseif forms_by_hash[error.origin] is not defined %}
|
||||
<em>Unknown.</em>
|
||||
{% else %}
|
||||
{{ forms_by_hash[error.origin].name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if error.trace %}
|
||||
<span class="newline">Caused by:</span>
|
||||
{% for stacked in error.trace %}
|
||||
{{ profiler_dump(stacked) }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<em>Unknown.</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if data.default_data is defined %}
|
||||
<h3>
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-default_data" href="#">
|
||||
Default Data <span class="toggle-icon"></span>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div id="{{ data.id }}-default_data">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="180">Property</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Model Format</th>
|
||||
<td>
|
||||
{% if data.default_data.model is defined %}
|
||||
{{ profiler_dump(data.default_data.seek('model')) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Normalized Format</th>
|
||||
<td>{{ profiler_dump(data.default_data.seek('norm')) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">View Format</th>
|
||||
<td>
|
||||
{% if data.default_data.view is defined %}
|
||||
{{ profiler_dump(data.default_data.seek('view')) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if data.submitted_data is defined %}
|
||||
<h3>
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-submitted_data" href="#">
|
||||
Submitted Data <span class="toggle-icon"></span>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div id="{{ data.id }}-submitted_data">
|
||||
{% if data.submitted_data.norm is defined %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="180">Property</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">View Format</th>
|
||||
<td>
|
||||
{% if data.submitted_data.view is defined %}
|
||||
{{ profiler_dump(data.submitted_data.seek('view')) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Normalized Format</th>
|
||||
<td>{{ profiler_dump(data.submitted_data.seek('norm')) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="font-normal" scope="row">Model Format</th>
|
||||
<td>
|
||||
{% if data.submitted_data.model is defined %}
|
||||
{{ profiler_dump(data.submitted_data.seek('model')) }}
|
||||
{% else %}
|
||||
<em class="font-normal text-muted">same as normalized format</em>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<p>This form was not submitted.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if data.passed_options is defined %}
|
||||
<h3>
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-passed_options" href="#">
|
||||
Passed Options <span class="toggle-icon"></span>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div id="{{ data.id }}-passed_options">
|
||||
{% if data.passed_options|length %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="180">Option</th>
|
||||
<th>Passed Value</th>
|
||||
<th>Resolved Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for option, value in data.passed_options %}
|
||||
<tr>
|
||||
<th>{{ option }}</th>
|
||||
<td>{{ profiler_dump(value) }}</td>
|
||||
<td>
|
||||
{# values can be stubs #}
|
||||
{% set option_value = value.value|default(value) %}
|
||||
{% set resolved_option_value = data.resolved_options[option].value|default(data.resolved_options[option]) %}
|
||||
{% if resolved_option_value == option_value %}
|
||||
<em class="font-normal text-muted">same as passed value</em>
|
||||
{% else %}
|
||||
{{ profiler_dump(data.resolved_options.seek(option)) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<p>No options were passed when constructing this form.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if data.resolved_options is defined %}
|
||||
<h3>
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-resolved_options" href="#">
|
||||
Resolved Options <span class="toggle-icon"></span>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div id="{{ data.id }}-resolved_options" class="hidden">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="180">Option</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for option, value in data.resolved_options %}
|
||||
<tr>
|
||||
<th scope="row">{{ option }}</th>
|
||||
<td>{{ profiler_dump(value) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if data.view_vars is defined %}
|
||||
<h3>
|
||||
<a class="toggle-button" data-toggle-target-id="{{ data.id }}-view_vars" href="#">
|
||||
View Variables <span class="toggle-icon"></span>
|
||||
</a>
|
||||
</h3>
|
||||
|
||||
<div id="{{ data.id }}-view_vars" class="hidden">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="180">Variable</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for variable, value in data.view_vars %}
|
||||
<tr>
|
||||
<th scope="row">{{ variable }}</th>
|
||||
<td>{{ profiler_dump(value) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% for childName, childData in data.children %}
|
||||
{{ tree.form_tree_details(childName, childData, forms_by_hash) }}
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
131
vendor/symfony/web-profiler-bundle/Resources/views/Collector/http_client.html.twig
vendored
Normal file
131
vendor/symfony/web-profiler-bundle/Resources/views/Collector/http_client.html.twig
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.requestCount %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/http-client.svg') }}
|
||||
{% set status_color = '' %}
|
||||
<span class="sf-toolbar-value">{{ collector.requestCount }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Total requests</b>
|
||||
<span>{{ collector.requestCount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>HTTP errors</b>
|
||||
<span class="sf-toolbar-status {{ collector.errorCount > 0 ? 'sf-toolbar-status-red' }}">{{ collector.errorCount }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ collector.requestCount == 0 ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/http-client.svg') }}</span>
|
||||
<strong>HTTP Client</strong>
|
||||
{% if collector.requestCount %}
|
||||
<span class="count">
|
||||
{{ collector.requestCount }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>HTTP Client</h2>
|
||||
{% if collector.requestCount == 0 %}
|
||||
<div class="empty">
|
||||
<p>No HTTP requests were made.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.requestCount }}</span>
|
||||
<span class="label">Total requests</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.errorCount }}</span>
|
||||
<span class="label">HTTP errors</span>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Clients</h2>
|
||||
<div class="sf-tabs">
|
||||
{% for name, client in collector.clients %}
|
||||
<div class="tab {{ client.traces|length == 0 ? 'disabled' }}">
|
||||
<h3 class="tab-title">{{ name }} <span class="badge">{{ client.traces|length }}</span></h3>
|
||||
<div class="tab-content">
|
||||
{% if client.traces|length == 0 %}
|
||||
<div class="empty">
|
||||
<p>No requests were made with the "{{ name }}" service.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<h4>Requests</h4>
|
||||
{% for trace in client.traces %}
|
||||
{% set profiler_token = '' %}
|
||||
{% set profiler_link = '' %}
|
||||
{% if trace.info.response_headers is defined %}
|
||||
{% for header in trace.info.response_headers %}
|
||||
{% if header matches '/^x-debug-token: .*$/i' %}
|
||||
{% set profiler_token = (header.getValue | slice('x-debug-token: ' | length)) %}
|
||||
{% endif %}
|
||||
{% if header matches '/^x-debug-token-link: .*$/i' %}
|
||||
{% set profiler_link = (header.getValue | slice('x-debug-token-link: ' | length)) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<span class="label">{{ trace.method }}</span>
|
||||
</th>
|
||||
<th class="full-width">
|
||||
{{ trace.url }}
|
||||
{% if trace.options is not empty %}
|
||||
{{ profiler_dump(trace.options, maxDepth=1) }}
|
||||
{% endif %}
|
||||
</th>
|
||||
{% if profiler_token and profiler_link %}
|
||||
<th>
|
||||
Profile
|
||||
</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
{% if trace.http_code >= 500 %}
|
||||
{% set responseStatus = 'error' %}
|
||||
{% elseif trace.http_code >= 400 %}
|
||||
{% set responseStatus = 'warning' %}
|
||||
{% else %}
|
||||
{% set responseStatus = 'success' %}
|
||||
{% endif %}
|
||||
<span class="label status-{{ responseStatus }}">
|
||||
{{ trace.http_code }}
|
||||
</span>
|
||||
</th>
|
||||
<td>
|
||||
{{ profiler_dump(trace.info, maxDepth=1) }}
|
||||
</td>
|
||||
{% if profiler_token and profiler_link %}
|
||||
<td>
|
||||
<span><a href="{{ profiler_link }}" target="_blank">{{ profiler_token }}</a></span>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
274
vendor/symfony/web-profiler-bundle/Resources/views/Collector/logger.html.twig
vendored
Normal file
274
vendor/symfony/web-profiler-bundle/Resources/views/Collector/logger.html.twig
vendored
Normal file
@ -0,0 +1,274 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
|
||||
{% set icon %}
|
||||
{% set status_color = collector.counterrors ? 'red' : collector.countwarnings ? 'yellow' : 'none' %}
|
||||
{{ include('@WebProfiler/Icon/logger.svg') }}
|
||||
<span class="sf-toolbar-value">{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Errors</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.counterrors ? 'red' }}">{{ collector.counterrors|default(0) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Warnings</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countwarnings ? 'yellow' }}">{{ collector.countwarnings|default(0) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Deprecations</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countdeprecations ? 'none' }}">{{ collector.countdeprecations|default(0) }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label label-status-{{ collector.counterrors ? 'error' : collector.countwarnings ? 'warning' : 'none' }} {{ collector.logs is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/logger.svg') }}</span>
|
||||
<strong>Logs</strong>
|
||||
{% if collector.counterrors or collector.countdeprecations or collector.countwarnings %}
|
||||
<span class="count">
|
||||
<span>{{ collector.counterrors ?: (collector.countdeprecations + collector.countwarnings) }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Log Messages</h2>
|
||||
|
||||
{% if collector.processedLogs is empty %}
|
||||
<div class="empty">
|
||||
<p>No log messages available.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% set has_error_logs = collector.processedLogs|column('type')|filter(type => 'error' == type)|length > 0 %}
|
||||
{% set has_deprecation_logs = collector.processedLogs|column('type')|filter(type => 'deprecation' == type)|length > 0 %}
|
||||
|
||||
{% set filters = collector.filters %}
|
||||
<div class="log-filters">
|
||||
<div id="log-filter-type" class="log-filter">
|
||||
<ul class="tab-navigation">
|
||||
<li class="{{ not has_error_logs and not has_deprecation_logs ? 'active' }}">
|
||||
<input {{ not has_error_logs and not has_deprecation_logs ? 'checked' }} type="radio" id="filter-log-type-all" name="filter-log-type" value="all">
|
||||
<label for="filter-log-type-all">All messages</label>
|
||||
</li>
|
||||
|
||||
<li class="{{ has_error_logs ? 'active' }}">
|
||||
<input {{ has_error_logs ? 'checked' }} type="radio" id="filter-log-type-error" name="filter-log-type" value="error">
|
||||
<label for="filter-log-type-error">
|
||||
Errors
|
||||
<span class="badge status-{{ collector.counterrors ? 'error' }}">{{ collector.counterrors|default(0) }}</span>
|
||||
</label>
|
||||
</li>
|
||||
|
||||
<li class="{{ not has_error_logs and has_deprecation_logs ? 'active' }}">
|
||||
<input {{ not has_error_logs and has_deprecation_logs ? 'checked' }} type="radio" id="filter-log-type-deprecation" name="filter-log-type" value="deprecation">
|
||||
<label for="filter-log-type-deprecation">
|
||||
Deprecations
|
||||
<span class="badge status-{{ collector.countdeprecations ? 'warning' }}">{{ collector.countdeprecations|default(0) }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<details id="log-filter-priority" class="log-filter">
|
||||
<summary>
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/filter.svg') }}</span>
|
||||
Level (<span class="filter-active-num">{{ filters.priority|length - 1 }}</span>)
|
||||
</summary>
|
||||
|
||||
<div class="log-filter-content">
|
||||
<div class="filter-select-all-or-none">
|
||||
<button type="button" class="btn btn-link select-all">Select All</button>
|
||||
<button type="button" class="btn btn-link select-none">Select None</button>
|
||||
</div>
|
||||
|
||||
{% for label, value in filters.priority %}
|
||||
<div class="log-filter-option">
|
||||
<input {{ 'debug' != value ? 'checked' }} type="checkbox" id="filter-log-level-{{ value }}" name="filter-log-level-{{ value }}" value="{{ value }}">
|
||||
<label for="filter-log-level-{{ value }}">{{ label }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details id="log-filter-channel" class="log-filter">
|
||||
<summary>
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/filter.svg') }}</span>
|
||||
Channel (<span class="filter-active-num">{{ filters.channel|length - 1 }}</span>)
|
||||
</summary>
|
||||
|
||||
<div class="log-filter-content">
|
||||
<div class="filter-select-all-or-none">
|
||||
<button type="button" class="btn btn-link select-all">Select All</button>
|
||||
<button type="button" class="btn btn-link select-none">Select None</button>
|
||||
</div>
|
||||
|
||||
{% for value in filters.channel %}
|
||||
<div class="log-filter-option">
|
||||
<input {{ 'event' != value ? 'checked' }} type="checkbox" id="filter-log-channel-{{ value }}" name="filter-log-channel-{{ value }}" value="{{ value }}">
|
||||
<label for="filter-log-channel-{{ value }}">{{ value|title }}</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<table class="logs">
|
||||
<colgroup>
|
||||
<col width="140px">
|
||||
<col>
|
||||
</colgroup>
|
||||
|
||||
<thead>
|
||||
<th>Time</th>
|
||||
<th>Message</th>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for log in collector.processedLogs %}
|
||||
{% set css_class = 'error' == log.type ? 'error'
|
||||
: (log.priorityName == 'WARNING' or 'deprecation' == log.type) ? 'warning'
|
||||
: 'silenced' == log.type ? 'silenced'
|
||||
%}
|
||||
<tr class="log-status-{{ css_class }}" data-type="{{ log.type }}" data-priority="{{ log.priority }}" data-channel="{{ log.channel }}" style="{{ 'event' == log.channel or 'DEBUG' == log.priorityName ? 'display: none' }}">
|
||||
<td class="log-timestamp">
|
||||
<time title="{{ log.timestamp|date('r') }}" datetime="{{ log.timestamp|date('c') }}">
|
||||
{{ log.timestamp|date('H:i:s.v') }}
|
||||
</time>
|
||||
|
||||
{% if log.type in ['error', 'deprecation', 'silenced'] or 'WARNING' == log.priorityName %}
|
||||
<span class="log-type-badge badge badge-{{ css_class }}">
|
||||
{% if 'error' == log.type or 'WARNING' == log.priorityName %}
|
||||
{{ log.priorityName|lower }}
|
||||
{% else %}
|
||||
{{ log.type|lower }}
|
||||
{% endif %}
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="log-type-badge badge badge-{{ css_class }}">
|
||||
{{ log.priorityName|lower }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="font-normal">
|
||||
{{ helper.render_log_message('debug', loop.index, log) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="no-logs-message empty">
|
||||
<p>There are no log messages.</p>
|
||||
</div>
|
||||
|
||||
<script>Sfjs.initializeLogsTable();</script>
|
||||
{% endif %}
|
||||
|
||||
{% set compilerLogTotal = 0 %}
|
||||
{% for logs in collector.compilerLogs %}
|
||||
{% set compilerLogTotal = compilerLogTotal + logs|length %}
|
||||
{% endfor %}
|
||||
|
||||
<details class="container-compilation-logs">
|
||||
<summary>
|
||||
<h4>Container Compilation Logs <span class="text-muted">({{ compilerLogTotal }})</span></h4>
|
||||
<p class="text-muted">Log messages generated during the compilation of the service container.</p>
|
||||
</summary>
|
||||
|
||||
{% if collector.compilerLogs is empty %}
|
||||
<div class="empty">
|
||||
<p>There are no compiler log messages.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<table class="container-logs">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Messages</th>
|
||||
<th class="full-width">Class</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for class, logs in collector.compilerLogs %}
|
||||
<tr>
|
||||
<td class="font-normal text-right">{{ logs|length }}</td>
|
||||
<td class="font-normal">
|
||||
{% set context_id = 'context-compiler-' ~ loop.index %}
|
||||
|
||||
<button type="button" class="btn btn-link sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="{{ class }}">{{ class }}</button>
|
||||
|
||||
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
<ul class="break-long-words">
|
||||
{% for log in logs %}
|
||||
<li>{{ profiler_dump_log(log.message) }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</details>
|
||||
{% endblock %}
|
||||
|
||||
{% macro render_log_message(category, log_index, log) %}
|
||||
{% set has_context = log.context is defined and log.context is not empty %}
|
||||
{% set has_trace = log.context.exception.trace is defined %}
|
||||
|
||||
{% if not has_context %}
|
||||
{{ profiler_dump_log(log.message) }}
|
||||
{% else %}
|
||||
{{ profiler_dump_log(log.message, log.context) }}
|
||||
{% endif %}
|
||||
|
||||
<div class="log-metadata">
|
||||
{% if log.channel %}
|
||||
<span class="badge">{{ log.channel }}</span>
|
||||
{% endif %}
|
||||
|
||||
{% if log.errorCount is defined and log.errorCount > 1 %}
|
||||
<span class="log-num-occurrences">{{ log.errorCount }} times</span>
|
||||
{% endif %}
|
||||
|
||||
{% if has_context %}
|
||||
{% set context_id = 'context-' ~ category ~ '-' ~ log_index %}
|
||||
<span><button type="button" class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ context_id }}" data-toggle-alt-content="Hide context">Show context</button></span>
|
||||
{% endif %}
|
||||
|
||||
{% if has_trace %}
|
||||
{% set trace_id = 'trace-' ~ category ~ '-' ~ log_index %}
|
||||
<span><button type="button" class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ trace_id }}" data-toggle-alt-content="Hide trace">Show trace</button></span>
|
||||
|
||||
<div id="{{ trace_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context.exception.trace, maxDepth=1) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if has_context %}
|
||||
<div id="{{ context_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context, maxDepth=1) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if has_trace %}
|
||||
<div id="{{ trace_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(log.context.exception.trace, maxDepth=1) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
217
vendor/symfony/web-profiler-bundle/Resources/views/Collector/mailer.html.twig
vendored
Normal file
217
vendor/symfony/web-profiler-bundle/Resources/views/Collector/mailer.html.twig
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% set events = collector.events %}
|
||||
|
||||
{% if events.messages|length %}
|
||||
{% set icon %}
|
||||
{% include('@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 head %}
|
||||
{{ parent() }}
|
||||
<style type="text/css">
|
||||
/* utility classes */
|
||||
.m-t-0 { margin-top: 0 !important; }
|
||||
.m-t-10 { margin-top: 10px !important; }
|
||||
|
||||
/* basic grid */
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
.col {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 1px;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.col-4 {
|
||||
flex: 0 0 33.333333%;
|
||||
max-width: 33.333333%;
|
||||
}
|
||||
|
||||
/* small tabs */
|
||||
.sf-tabs-sm .tab-navigation li {
|
||||
font-size: 14px;
|
||||
padding: .3em .5em;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
{% set events = collector.events %}
|
||||
|
||||
<span class="label {{ events.messages is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/mailer.svg') }}</span>
|
||||
|
||||
<strong>E-mails</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">
|
||||
<p>No emails were sent.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="metrics">
|
||||
<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>
|
||||
|
||||
{% for transport in events.transports %}
|
||||
<div class="card-block">
|
||||
<div class="sf-tabs sf-tabs-sm">
|
||||
{% for event in events.events(transport) %}
|
||||
{% set message = event.message %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Email {{ event.isQueued() ? 'queued' : 'sent via ' ~ transport }}</h3>
|
||||
<div class="tab-content">
|
||||
<div class="card">
|
||||
{% if message.headers is not defined %}
|
||||
{# RawMessage instance #}
|
||||
<div class="card-block">
|
||||
<pre class="prewrap" style="max-height: 600px">{{ message.toString() }}</pre>
|
||||
</div>
|
||||
{% else %}
|
||||
{# Message instance #}
|
||||
<div class="card-block">
|
||||
<div class="sf-tabs sf-tabs-sm">
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Headers</h3>
|
||||
<div class="tab-content">
|
||||
<span class="label">Subject</span>
|
||||
<h2 class="m-t-10">{{ message.headers.get('subject').bodyAsString() ?? '(empty)' }}</h2>
|
||||
<div class="row">
|
||||
<div class="col col-4">
|
||||
<span class="label">From</span>
|
||||
<pre class="prewrap">{{ (message.headers.get('from').bodyAsString() ?? '(empty)')|replace({'From:': ''}) }}</pre>
|
||||
|
||||
<span class="label">To</span>
|
||||
<pre class="prewrap">{{ (message.headers.get('to').bodyAsString() ?? '(empty)')|replace({'To:': ''}) }}</pre>
|
||||
</div>
|
||||
<div class="col">
|
||||
<span class="label">Headers</span>
|
||||
<pre class="prewrap">{% for header in message.headers.all|filter(header => (header.name ?? '') not in ['Subject', 'From', 'To']) %}
|
||||
{{- header.toString }}
|
||||
{%~ endfor %}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if message.htmlBody is defined %}
|
||||
{# Email instance #}
|
||||
{% set htmlBody = message.htmlBody() %}
|
||||
{% if htmlBody is not null %}
|
||||
<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>
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">HTML Content</h3>
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">
|
||||
{%- if message.htmlCharset() %}
|
||||
{{- htmlBody|convert_encoding('UTF-8', message.htmlCharset()) }}
|
||||
{%- else %}
|
||||
{{- htmlBody }}
|
||||
{%- endif -%}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% set textBody = message.textBody() %}
|
||||
{% if textBody is not null %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Text Content</h3>
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">
|
||||
{%- if message.textCharset() %}
|
||||
{{- textBody|convert_encoding('UTF-8', message.textCharset()) }}
|
||||
{%- else %}
|
||||
{{- textBody }}
|
||||
{%- endif -%}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for attachment in message.attachments %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Attachment #{{ loop.index }}</h3>
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">{{ attachment.toString() }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Parts Hierarchy</h3>
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">{{ message.body().asDebugString() }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Raw</h3>
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">{{ message.toString() }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
24
vendor/symfony/web-profiler-bundle/Resources/views/Collector/memory.html.twig
vendored
Normal file
24
vendor/symfony/web-profiler-bundle/Resources/views/Collector/memory.html.twig
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% set icon %}
|
||||
{% set status_color = (collector.memory / 1024 / 1024) > 50 ? 'yellow' %}
|
||||
{{ include('@WebProfiler/Icon/memory.svg') }}
|
||||
<span class="sf-toolbar-value">{{ '%.1f'|format(collector.memory / 1024 / 1024) }}</span>
|
||||
<span class="sf-toolbar-label">MiB</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Peak memory usage</b>
|
||||
<span>{{ '%.1f'|format(collector.memory / 1024 / 1024) }} MiB</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>PHP memory limit</b>
|
||||
<span>{{ collector.memoryLimit == -1 ? 'Unlimited' : '%.0f MiB'|format(collector.memoryLimit / 1024 / 1024) }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, name: 'time', status: status_color }) }}
|
||||
{% endblock %}
|
201
vendor/symfony/web-profiler-bundle/Resources/views/Collector/messenger.html.twig
vendored
Normal file
201
vendor/symfony/web-profiler-bundle/Resources/views/Collector/messenger.html.twig
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.messages|length > 0 %}
|
||||
{% set status_color = collector.exceptionsCount ? 'red' %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/messenger.svg') }}
|
||||
<span class="sf-toolbar-value">{{ collector.messages|length }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
{% for bus in collector.buses %}
|
||||
{% set exceptionsCount = collector.exceptionsCount(bus) %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>{{ bus }}</b>
|
||||
<span
|
||||
title="{{ exceptionsCount }} message(s) with exceptions"
|
||||
class="sf-toolbar-status sf-toolbar-status-{{ exceptionsCount ? 'red' }}"
|
||||
>
|
||||
{{ collector.messages(bus)|length }}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: 'messenger', status: status_color }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label{{ collector.exceptionsCount ? ' label-status-error' }}{{ collector.messages is empty ? ' disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/messenger.svg') }}</span>
|
||||
<strong>Messages</strong>
|
||||
{% if collector.exceptionsCount > 0 %}
|
||||
<span class="count">
|
||||
<span>{{ collector.exceptionsCount }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ parent() }}
|
||||
<style>
|
||||
.message-item thead th { position: relative; cursor: pointer; user-select: none; padding-right: 35px; }
|
||||
.message-item tbody tr td:first-child { width: 170px; }
|
||||
|
||||
.message-item .label { float: right; padding: 1px 5px; opacity: .75; margin-left: 5px; }
|
||||
.message-item .toggle-button { position: absolute; right: 6px; top: 6px; opacity: .5; pointer-events: none }
|
||||
.message-item .icon svg { height: 24px; width: 24px; }
|
||||
|
||||
.message-item .sf-toggle-off .icon-close, .sf-toggle-on .icon-open { display: none; }
|
||||
.message-item .sf-toggle-off .icon-open, .sf-toggle-on .icon-close { display: block; }
|
||||
|
||||
.message-bus .badge.status-some-errors { line-height: 16px; border-bottom: 2px solid #B0413E; }
|
||||
|
||||
.message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; }
|
||||
td.message-bus-dispatch-caller { background: #f1f2f3; }
|
||||
.theme-dark td.message-bus-dispatch-caller { background: var(--base-1); }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{% import _self as helper %}
|
||||
|
||||
<h2>Messages</h2>
|
||||
|
||||
{% if collector.messages is empty %}
|
||||
<div class="empty">
|
||||
<p>No messages have been collected.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="sf-tabs message-bus">
|
||||
<div class="tab">
|
||||
{% set messages = collector.messages %}
|
||||
{% set exceptionsCount = collector.exceptionsCount %}
|
||||
<h3 class="tab-title">All<span class="badge {{ exceptionsCount ? exceptionsCount == messages|length ? 'status-error' : 'status-some-errors' }}">{{ messages|length }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<p class="text-muted">Ordered list of dispatched messages across all your buses</p>
|
||||
{{ helper.render_bus_messages(messages, true) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for bus in collector.buses %}
|
||||
<div class="tab message-bus">
|
||||
{% set messages = collector.messages(bus) %}
|
||||
{% set exceptionsCount = collector.exceptionsCount(bus) %}
|
||||
<h3 class="tab-title">{{ bus }}<span class="badge {{ exceptionsCount ? exceptionsCount == messages|length ? 'status-error' : 'status-some-errors' }}">{{ messages|length }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<p class="text-muted">Ordered list of messages dispatched on the <code>{{ bus }}</code> bus</p>
|
||||
{{ helper.render_bus_messages(messages) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% macro render_bus_messages(messages, showBus = false) %}
|
||||
{% set discr = random() %}
|
||||
{% for dispatchCall in messages %}
|
||||
<table class="message-item">
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2" class="sf-toggle"
|
||||
data-toggle-selector="#message-item-{{ discr }}-{{ loop.index0 }}-details"
|
||||
data-toggle-initial="{{ loop.first ? 'display' }}"
|
||||
>
|
||||
<span class="dump-inline">{{ profiler_dump(dispatchCall.message.type) }}</span>
|
||||
{% if showBus %}
|
||||
<span class="label">{{ dispatchCall.bus }}</span>
|
||||
{% endif %}
|
||||
{% if dispatchCall.exception is defined %}
|
||||
<span class="label status-error">exception</span>
|
||||
{% endif %}
|
||||
<a class="toggle-button">
|
||||
<span class="icon icon-close">{{ include('@WebProfiler/images/icon-minus-square.svg') }}</span>
|
||||
<span class="icon icon-open">{{ include('@WebProfiler/images/icon-plus-square.svg') }}</span>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="message-item-{{ discr }}-{{ loop.index0 }}-details" class="sf-toggle-content">
|
||||
<tr>
|
||||
<td colspan="2" class="message-bus-dispatch-caller">
|
||||
<span class="metadata">In
|
||||
{% set caller = dispatchCall.caller %}
|
||||
{% if caller.line %}
|
||||
{% set link = caller.file|file_link(caller.line) %}
|
||||
{% if link %}
|
||||
<a href="{{ link }}" title="{{ caller.file }}">{{ caller.name }}</a>
|
||||
{% else %}
|
||||
<abbr title="{{ caller.file }}">{{ caller.name }}</abbr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ caller.name }}
|
||||
{% endif %}
|
||||
line <a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ discr }}-{{ loop.index0 }}">{{ caller.line }}</a>
|
||||
</span>
|
||||
|
||||
<div class="hidden" id="sf-trace-{{ discr }}-{{ loop.index0 }}">
|
||||
<div class="trace">
|
||||
{{ caller.file|file_excerpt(caller.line)|replace({
|
||||
'#DD0000': 'var(--highlight-string)',
|
||||
'#007700': 'var(--highlight-keyword)',
|
||||
'#0000BB': 'var(--highlight-default)',
|
||||
'#FF8000': 'var(--highlight-comment)'
|
||||
})|raw }}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% if showBus %}
|
||||
<tr>
|
||||
<td class="text-bold">Bus</td>
|
||||
<td>{{ dispatchCall.bus }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td class="text-bold">Message</td>
|
||||
<td>{{ profiler_dump(dispatchCall.message.value, maxDepth=2) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-bold">Envelope stamps <span class="text-muted">when dispatching</span></td>
|
||||
<td>
|
||||
{% for item in dispatchCall.stamps %}
|
||||
{{ profiler_dump(item) }}
|
||||
{% else %}
|
||||
<span class="text-muted">No items</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if dispatchCall.stamps_after_dispatch is defined %}
|
||||
<tr>
|
||||
<td class="text-bold">Envelope stamps <span class="text-muted">after dispatch</span></td>
|
||||
<td>
|
||||
{% for item in dispatchCall.stamps_after_dispatch %}
|
||||
{{ profiler_dump(item) }}
|
||||
{% else %}
|
||||
<span class="text-muted">No items</span>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if dispatchCall.exception is defined %}
|
||||
<tr>
|
||||
<td class="text-bold">Exception</td>
|
||||
<td>
|
||||
{{ profiler_dump(dispatchCall.exception.value, maxDepth=1) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
168
vendor/symfony/web-profiler-bundle/Resources/views/Collector/notifier.html.twig
vendored
Normal file
168
vendor/symfony/web-profiler-bundle/Resources/views/Collector/notifier.html.twig
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% set events = collector.events %}
|
||||
|
||||
{% if events.messages|length %}
|
||||
{% set icon %}
|
||||
{% include('@WebProfiler/Icon/notifier.svg') %}
|
||||
<span class="sf-toolbar-value">{{ events.messages|length }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Sent notifications</b>
|
||||
<span class="sf-toolbar-status">{{ events.messages|length }}</span>
|
||||
</div>
|
||||
|
||||
{% for transport in events.transports %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>{{ transport }}</b>
|
||||
<span class="sf-toolbar-status">{{ events.messages(transport)|length }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': profiler_url }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ parent() }}
|
||||
<style type="text/css">
|
||||
/* utility classes */
|
||||
.m-t-0 { margin-top: 0 !important; }
|
||||
.m-t-10 { margin-top: 10px !important; }
|
||||
|
||||
/* basic grid */
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-right: -15px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
.col {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
max-width: 100%;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 1px;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
.col-4 {
|
||||
flex: 0 0 33.333333%;
|
||||
max-width: 33.333333%;
|
||||
}
|
||||
|
||||
/* small tabs */
|
||||
.sf-tabs-sm .tab-navigation li {
|
||||
font-size: 14px;
|
||||
padding: .3em .5em;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
{% set events = collector.events %}
|
||||
|
||||
<span class="label {{ events.messages|length ? '' : 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/notifier.svg') }}</span>
|
||||
|
||||
<strong>Notifications</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>Notifications</h2>
|
||||
|
||||
{% if not events.messages|length %}
|
||||
<div class="empty">
|
||||
<p>No notifications were sent.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="metrics">
|
||||
{% for transport in events.transports %}
|
||||
<div class="metric">
|
||||
<span class="value">{{ events.messages(transport)|length }}</span>
|
||||
<span class="label">{{ transport }}</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% for transport in events.transports %}
|
||||
<h3>{{ transport }}</h3>
|
||||
|
||||
<div class="card-block">
|
||||
<div class="sf-tabs sf-tabs-sm">
|
||||
{% for event in events.events(transport) %}
|
||||
{% set message = event.message %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Message #{{ loop.index }} <small>({{ event.isQueued() ? 'queued' : 'sent' }})</small></h3>
|
||||
<div class="tab-content">
|
||||
<div class="card">
|
||||
<div class="card-block">
|
||||
<span class="label">Subject</span>
|
||||
<h2 class="m-t-10">{{ message.getSubject() ?? '(empty)' }}</h2>
|
||||
</div>
|
||||
{% if message.getNotification is defined %}
|
||||
<div class="card-block">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<span class="label">Content</span>
|
||||
<pre class="prewrap">{{ message.getNotification().getContent() ?? '(empty)' }}</pre>
|
||||
<span class="label">Importance</span>
|
||||
<pre class="prewrap">{{ message.getNotification().getImportance() }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="card-block">
|
||||
<div class="sf-tabs sf-tabs-sm">
|
||||
{% if message.getNotification is defined %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Notification</h3>
|
||||
{% set notification = event.message.getNotification() %}
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">
|
||||
{{- 'Subject: ' ~ notification.getSubject() }}<br/>
|
||||
{{- 'Content: ' ~ notification.getContent() }}<br/>
|
||||
{{- 'Importance: ' ~ notification.getImportance() }}<br/>
|
||||
{{- 'Emoji: ' ~ (notification.getEmoji() is empty ? '(empty)' : notification.getEmoji()) }}<br/>
|
||||
{{- 'Exception: ' ~ notification.getException() ?? '(empty)' }}<br/>
|
||||
{{- 'ExceptionAsString: ' ~ (notification.getExceptionAsString() is empty ? '(empty)' : notification.getExceptionAsString()) }}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Message Options</h3>
|
||||
<div class="tab-content">
|
||||
<pre class="prewrap" style="max-height: 600px">
|
||||
{%- if message.getOptions() is null %}
|
||||
{{- '(empty)' }}
|
||||
{%- else %}
|
||||
{{- message.getOptions()|json_encode(constant('JSON_PRETTY_PRINT')) }}
|
||||
{%- endif %}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
392
vendor/symfony/web-profiler-bundle/Resources/views/Collector/request.html.twig
vendored
Normal file
392
vendor/symfony/web-profiler-bundle/Resources/views/Collector/request.html.twig
vendored
Normal file
@ -0,0 +1,392 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% import _self as helper %}
|
||||
{% set request_handler %}
|
||||
{{ helper.set_handler(collector.controller) }}
|
||||
{% endset %}
|
||||
|
||||
{% if collector.redirect %}
|
||||
{% set redirect_handler %}
|
||||
{{ helper.set_handler(collector.redirect.controller, collector.redirect.route, 'GET' != collector.redirect.method ? collector.redirect.method) }}
|
||||
{% endset %}
|
||||
{% endif %}
|
||||
|
||||
{% if collector.forwardtoken %}
|
||||
{% set forward_profile = profile.childByToken(collector.forwardtoken) %}
|
||||
{% set forward_handler %}
|
||||
{{ helper.set_handler(forward_profile ? forward_profile.collector('request').controller : 'n/a') }}
|
||||
{% endset %}
|
||||
{% endif %}
|
||||
|
||||
{% set request_status_code_color = (collector.statuscode >= 400) ? 'red' : (collector.statuscode >= 300) ? 'yellow' : 'green' %}
|
||||
|
||||
{% set icon %}
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ request_status_code_color }}">{{ collector.statuscode }}</span>
|
||||
{% if collector.route %}
|
||||
{% if collector.redirect %}{{ include('@WebProfiler/Icon/redirect.svg') }}{% endif %}
|
||||
{% if collector.forwardtoken %}{{ include('@WebProfiler/Icon/forward.svg') }}{% endif %}
|
||||
<span class="sf-toolbar-label">{{ 'GET' != collector.method ? collector.method }} @</span>
|
||||
<span class="sf-toolbar-value sf-toolbar-info-piece-additional">{{ collector.route }}</span>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>HTTP status</b>
|
||||
<span>{{ collector.statuscode }} {{ collector.statustext }}</span>
|
||||
</div>
|
||||
|
||||
{% if 'GET' != collector.method -%}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Method</b>
|
||||
<span>{{ collector.method }}</span>
|
||||
</div>
|
||||
{%- endif %}
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Controller</b>
|
||||
<span>{{ request_handler }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Route name</b>
|
||||
<span>{{ collector.route|default('n/a') }}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Has session</b>
|
||||
<span>{% if collector.sessionmetadata|length %}yes{% else %}no{% endif %}</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Stateless Check</b>
|
||||
<span>{% if collector.statelesscheck %}yes{% else %}no{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if redirect_handler is defined -%}
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>
|
||||
<span class="sf-toolbar-redirection-status sf-toolbar-status-yellow">{{ collector.redirect.status_code }}</span>
|
||||
Redirect from
|
||||
</b>
|
||||
<span>
|
||||
{{ redirect_handler }}
|
||||
(<a href="{{ path('_profiler', { token: collector.redirect.token }) }}">{{ collector.redirect.token }}</a>)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if forward_handler is defined %}
|
||||
<div class="sf-toolbar-info-group">
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Forwarded to</b>
|
||||
<span>
|
||||
{{ forward_handler }}
|
||||
(<a href="{{ path('_profiler', { token: collector.forwardtoken }) }}">{{ collector.forwardtoken }}</a>)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/request.svg') }}</span>
|
||||
<strong>Request / Response</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{% import _self as helper %}
|
||||
|
||||
<h2>
|
||||
{{ helper.set_handler(collector.controller) }}
|
||||
</h2>
|
||||
|
||||
<div class="sf-tabs">
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Request</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<h3>GET Parameters</h3>
|
||||
|
||||
{% if collector.requestquery.all is empty %}
|
||||
<div class="empty">
|
||||
<p>No GET parameters</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestquery, maxDepth: 1 }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>POST Parameters</h3>
|
||||
|
||||
{% if collector.requestrequest.all is empty %}
|
||||
<div class="empty">
|
||||
<p>No POST parameters</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestrequest, maxDepth: 1 }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h4>Uploaded Files</h4>
|
||||
|
||||
{% if collector.requestfiles is empty %}
|
||||
<div class="empty">
|
||||
<p>No files were uploaded</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestfiles, maxDepth: 1 }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>Request Attributes</h3>
|
||||
|
||||
{% if collector.requestattributes.all is empty %}
|
||||
<div class="empty">
|
||||
<p>No attributes</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestattributes }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>Request Headers</h3>
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestheaders, labels: ['Header', 'Value'], maxDepth: 1 }, with_context = false) }}
|
||||
|
||||
<h3>Request Content</h3>
|
||||
|
||||
{% if collector.content == false %}
|
||||
<div class="empty">
|
||||
<p>Request content not available (it was retrieved as a resource).</p>
|
||||
</div>
|
||||
{% elseif collector.content %}
|
||||
<div class="sf-tabs">
|
||||
{% set prettyJson = collector.isJsonRequest ? collector.prettyJson : null %}
|
||||
{% if prettyJson is not null %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Pretty</h3>
|
||||
<div class="tab-content">
|
||||
<div class="card" style="max-height: 500px; overflow-y: auto;">
|
||||
<pre class="break-long-words">{{ prettyJson }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Raw</h3>
|
||||
<div class="tab-content">
|
||||
<div class="card">
|
||||
<pre class="break-long-words">{{ collector.content }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<p>No content</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Response</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<h3>Response Headers</h3>
|
||||
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responseheaders, labels: ['Header', 'Value'], maxDepth: 1 }, with_context = false) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ collector.requestcookies.all is empty and collector.responsecookies.all is empty ? 'disabled' }}">
|
||||
<h3 class="tab-title">Cookies</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<h3>Request Cookies</h3>
|
||||
|
||||
{% if collector.requestcookies.all is empty %}
|
||||
<div class="empty">
|
||||
<p>No request cookies</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.requestcookies }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>Response Cookies</h3>
|
||||
|
||||
{% if collector.responsecookies.all is empty %}
|
||||
<div class="empty">
|
||||
<p>No response cookies</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.responsecookies }, with_context = true) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ collector.sessionmetadata is empty ? 'disabled' }}">
|
||||
<h3 class="tab-title">Session{% if collector.sessionusages is not empty %} <span class="badge">{{ collector.sessionusages|length }}</span>{% endif %}</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<h3>Session Metadata</h3>
|
||||
|
||||
{% if collector.sessionmetadata is empty %}
|
||||
<div class="empty">
|
||||
<p>No session metadata</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.sessionmetadata }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>Session Attributes</h3>
|
||||
|
||||
{% if collector.sessionattributes is empty %}
|
||||
<div class="empty">
|
||||
<p>No session attributes</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.sessionattributes, labels: ['Attribute', 'Value'] }, with_context = false) }}
|
||||
{% endif %}
|
||||
|
||||
<h3>Session Usage</h3>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.sessionusages|length }}</span>
|
||||
<span class="label">Usages</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ include('@WebProfiler/Icon/' ~ (collector.statelesscheck ? 'yes' : 'no') ~ '.svg') }}</span>
|
||||
<span class="label">Stateless check enabled</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if collector.sessionusages is empty %}
|
||||
<div class="empty">
|
||||
<p>Session not used.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<table class="session_usages">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="full-width">Usage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for key, usage in collector.sessionusages %}
|
||||
<tr>
|
||||
<td class="font-normal">
|
||||
{%- set link = usage.file|file_link(usage.line) %}
|
||||
{%- if link %}<a href="{{ link }}" title="{{ usage.name }}">{% else %}<span title="{{ usage.name }}">{% endif %}
|
||||
{{ usage.name }}
|
||||
{%- if link %}</a>{% else %}</span>{% endif %}
|
||||
<div class="text-small font-normal">
|
||||
{% set usage_id = 'session-usage-trace-' ~ key %}
|
||||
<a class="btn btn-link text-small sf-toggle" data-toggle-selector="#{{ usage_id }}" data-toggle-alt-content="Hide trace">Show trace</a>
|
||||
</div>
|
||||
<div id="{{ usage_id }}" class="context sf-toggle-content sf-toggle-hidden">
|
||||
{{ profiler_dump(usage.trace, maxDepth=2) }}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ collector.flashes is empty ? 'disabled' }}">
|
||||
<h3 class="tab-title">Flashes</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<h3>Flashes</h3>
|
||||
|
||||
{% if collector.flashes is empty %}
|
||||
<div class="empty">
|
||||
<p>No flash messages were created.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ include('@WebProfiler/Profiler/table.html.twig', { data: collector.flashes }, with_context = false) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Server Parameters</h3>
|
||||
<div class="tab-content">
|
||||
<h3>Server Parameters</h3>
|
||||
<h4>Defined in .env</h4>
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: collector.dotenvvars }, with_context = false) }}
|
||||
|
||||
<h4>Defined as regular env variables</h4>
|
||||
{% set requestserver = [] %}
|
||||
{% for key, value in collector.requestserver|filter((_, key) => key not in collector.dotenvvars.keys) %}
|
||||
{% set requestserver = requestserver|merge({(key): value}) %}
|
||||
{% endfor %}
|
||||
{{ include('@WebProfiler/Profiler/table.html.twig', { data: requestserver }, with_context = false) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if profile.parent %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Parent Request</h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<h3>
|
||||
<a href="{{ path('_profiler', { token: profile.parent.token }) }}">Return to parent request</a>
|
||||
<small>(token = {{ profile.parent.token }})</small>
|
||||
</h3>
|
||||
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: profile.parent.getcollector('request').requestattributes }, with_context = false) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.children|length %}
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Sub Requests <span class="badge">{{ profile.children|length }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
{% for child in profile.children %}
|
||||
<h3>
|
||||
{{ helper.set_handler(child.getcollector('request').controller) }}
|
||||
<small>(token = <a href="{{ path('_profiler', { token: child.token }) }}">{{ child.token }}</a>)</small>
|
||||
</h3>
|
||||
|
||||
{{ include('@WebProfiler/Profiler/bag.html.twig', { bag: child.getcollector('request').requestattributes }, with_context = false) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro set_handler(controller, route, method) %}
|
||||
{% if controller.class is defined -%}
|
||||
{%- if method|default(false) %}<span class="sf-toolbar-status sf-toolbar-redirection-method">{{ method }}</span>{% endif -%}
|
||||
{%- set link = controller.file|file_link(controller.line) %}
|
||||
{%- if link %}<a href="{{ link }}" title="{{ controller.class }}">{% else %}<span title="{{ controller.class }}">{% endif %}
|
||||
|
||||
{%- if route|default(false) -%}
|
||||
@{{ route }}
|
||||
{%- else -%}
|
||||
{{- controller.class|abbr_class|striptags -}}
|
||||
{{- controller.method ? ' :: ' ~ controller.method -}}
|
||||
{%- endif -%}
|
||||
|
||||
{%- if link %}</a>{% else %}</span>{% endif %}
|
||||
{%- else -%}
|
||||
<span>{{ route|default(controller) }}</span>
|
||||
{%- endif %}
|
||||
{% endmacro %}
|
14
vendor/symfony/web-profiler-bundle/Resources/views/Collector/router.html.twig
vendored
Normal file
14
vendor/symfony/web-profiler-bundle/Resources/views/Collector/router.html.twig
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/router.svg') }}</span>
|
||||
<strong>Routing</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{{ render(controller('web_profiler.controller.router::panelAction', { token: token })) }}
|
||||
{% endblock %}
|
64
vendor/symfony/web-profiler-bundle/Resources/views/Collector/time.css.twig
vendored
Normal file
64
vendor/symfony/web-profiler-bundle/Resources/views/Collector/time.css.twig
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
/* Legend */
|
||||
|
||||
.sf-profiler-timeline .legends .timeline-category {
|
||||
border: none;
|
||||
background: none;
|
||||
border-left: 1em solid transparent;
|
||||
line-height: 1em;
|
||||
margin: 0 1em 0 0;
|
||||
padding: 0 0.5em;
|
||||
display: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.sf-profiler-timeline .legends .timeline-category.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.sf-profiler-timeline .legends .timeline-category.present {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.timeline-graph {
|
||||
margin: 1em 0;
|
||||
width: 100%;
|
||||
background-color: var(--table-background);
|
||||
border: 1px solid var(--table-border);
|
||||
}
|
||||
|
||||
/* Typography */
|
||||
|
||||
.timeline-graph .timeline-label {
|
||||
font-family: var(--font-sans-serif);
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
font-weight: normal;
|
||||
fill: var(--color-text);
|
||||
}
|
||||
|
||||
.timeline-graph .timeline-label .timeline-sublabel {
|
||||
margin-left: 1em;
|
||||
fill: var(--color-muted);
|
||||
}
|
||||
|
||||
.timeline-graph .timeline-subrequest,
|
||||
.timeline-graph .timeline-border {
|
||||
fill: none;
|
||||
stroke: var(--table-border);
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
.timeline-graph .timeline-subrequest {
|
||||
fill: url(#subrequest);
|
||||
fill-opacity: 0.5;
|
||||
}
|
||||
|
||||
.timeline-subrequest-pattern {
|
||||
fill: var(--table-border);
|
||||
}
|
||||
|
||||
/* Timeline periods */
|
||||
|
||||
.timeline-graph .timeline-period {
|
||||
stroke-width: 0;
|
||||
}
|
214
vendor/symfony/web-profiler-bundle/Resources/views/Collector/time.html.twig
vendored
Normal file
214
vendor/symfony/web-profiler-bundle/Resources/views/Collector/time.html.twig
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% set has_time_events = collector.events|length > 0 %}
|
||||
{% set total_time = has_time_events ? '%.0f'|format(collector.duration) : 'n/a' %}
|
||||
{% set initialization_time = collector.events|length ? '%.0f'|format(collector.inittime) : 'n/a' %}
|
||||
{% set status_color = has_time_events and collector.duration > 1000 ? 'yellow' %}
|
||||
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/time.svg') }}
|
||||
<span class="sf-toolbar-value">{{ total_time }}</span>
|
||||
<span class="sf-toolbar-label">ms</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Total time</b>
|
||||
<span>{{ total_time }} ms</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Initialization time</b>
|
||||
<span>{{ initialization_time }} ms</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/time.svg') }}</span>
|
||||
<strong>Performance</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{% set has_time_events = collector.events|length > 0 %}
|
||||
<h2>Performance metrics</h2>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ '%.0f'|format(collector.duration) }} <span class="unit">ms</span></span>
|
||||
<span class="label">Total execution time</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ '%.0f'|format(collector.inittime) }} <span class="unit">ms</span></span>
|
||||
<span class="label">Symfony initialization</span>
|
||||
</div>
|
||||
|
||||
{% if profile.collectors.memory %}
|
||||
<div class="metric">
|
||||
<span class="value">{{ '%.2f'|format(profile.collectors.memory.memory / 1024 / 1024) }} <span class="unit">MiB</span></span>
|
||||
<span class="label">Peak memory usage</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if profile.children|length > 0 %}
|
||||
<div class="metric-divider"></div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ profile.children|length }}</span>
|
||||
<span class="label">Sub-Request{{ profile.children|length > 1 ? 's' }}</span>
|
||||
</div>
|
||||
|
||||
{% if has_time_events %}
|
||||
{% set subrequests_time = 0 %}
|
||||
{% for child in profile.children %}
|
||||
{% set subrequests_time = subrequests_time + child.getcollector('time').events.__section__.duration %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% set subrequests_time = 'n/a' %}
|
||||
{% endif %}
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ subrequests_time }} <span class="unit">ms</span></span>
|
||||
<span class="label">Sub-Request{{ profile.children|length > 1 ? 's' }} time</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2>Execution timeline</h2>
|
||||
|
||||
{% if not collector.isStopwatchInstalled() %}
|
||||
<div class="empty">
|
||||
<p>The Stopwatch component is not installed. If you want to see timing events, run: <code>composer require symfony/stopwatch</code>.</p>
|
||||
</div>
|
||||
{% elseif collector.events is empty %}
|
||||
<div class="empty">
|
||||
<p>No timing events have been recorded. Check that symfony/stopwatch is installed and debugging enabled in the kernel.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ block('panelContent') }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block panelContent %}
|
||||
<form id="timeline-control" action="" method="get">
|
||||
<input type="hidden" name="panel" value="time">
|
||||
<label for="threshold">Threshold</label>
|
||||
<input type="number" name="threshold" id="threshold" value="1" min="0" placeholder="1.1"> ms
|
||||
<span class="help">(timeline only displays events with a duration longer than this threshold)</span>
|
||||
</form>
|
||||
|
||||
{% if profile.parent %}
|
||||
<h3 class="dump-inline">
|
||||
Sub-Request {{ profiler_dump(profile.getcollector('request').requestattributes.get('_controller')) }}
|
||||
<small>
|
||||
{{ collector.events.__section__.duration }} ms
|
||||
<a class="newline" href="{{ path('_profiler', { token: profile.parent.token, panel: 'time' }) }}">Return to parent request</a>
|
||||
</small>
|
||||
</h3>
|
||||
{% elseif profile.children|length > 0 %}
|
||||
<h3>
|
||||
Main Request <small>{{ collector.events.__section__.duration }} ms</small>
|
||||
</h3>
|
||||
{% endif %}
|
||||
|
||||
{{ helper.display_timeline(token, collector.events, collector.events.__section__.origin) }}
|
||||
|
||||
{% if profile.children|length %}
|
||||
<p class="help">Note: sections with a striped background correspond to sub-requests.</p>
|
||||
|
||||
<h3>Sub-requests <small>({{ profile.children|length }})</small></h3>
|
||||
|
||||
{% for child in profile.children %}
|
||||
{% set events = child.getcollector('time').events %}
|
||||
<h4>
|
||||
<a href="{{ path('_profiler', { token: child.token, panel: 'time' }) }}">{{ child.getcollector('request').identifier }}</a>
|
||||
<small>{{ events.__section__.duration }} ms</small>
|
||||
</h4>
|
||||
|
||||
{{ helper.display_timeline(child.token, events, collector.events.__section__.origin) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<svg id="timeline-template" width="0" height="0">
|
||||
<defs>
|
||||
<pattern id="subrequest" class="timeline-subrequest-pattern" patternUnits="userSpaceOnUse" width="20" height="20" viewBox="0 0 40 40">
|
||||
<path d="M0 40L40 0H20L0 20M40 40V20L20 40"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
</svg>
|
||||
<style type="text/css">
|
||||
{% include '@WebProfiler/Collector/time.css.twig' %}
|
||||
</style>
|
||||
<script>
|
||||
{% include '@WebProfiler/Collector/time.js' %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% macro dump_request_data(token, events, origin) %}
|
||||
{% autoescape 'js' %}
|
||||
{% from _self import dump_events %}
|
||||
{
|
||||
id: "{{ token }}",
|
||||
left: {{ "%F"|format(events.__section__.origin - origin) }},
|
||||
end: "{{ '%F'|format(events.__section__.endtime) }}",
|
||||
events: [ {{ dump_events(events) }} ],
|
||||
}
|
||||
{% endautoescape %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro dump_events(events) %}
|
||||
{% autoescape 'js' %}
|
||||
{% for name, event in events %}
|
||||
{% if '__section__' != name %}
|
||||
{
|
||||
name: "{{ name }}",
|
||||
category: "{{ event.category }}",
|
||||
origin: {{ "%F"|format(event.origin) }},
|
||||
starttime: {{ "%F"|format(event.starttime) }},
|
||||
endtime: {{ "%F"|format(event.endtime) }},
|
||||
duration: {{ "%F"|format(event.duration) }},
|
||||
memory: {{ "%.1F"|format(event.memory / 1024 / 1024) }},
|
||||
elements: {},
|
||||
periods: [
|
||||
{%- for period in event.periods -%}
|
||||
{
|
||||
start: {{ "%F"|format(period.starttime) }},
|
||||
end: {{ "%F"|format(period.endtime) }},
|
||||
duration: {{ "%F"|format(period.duration) }},
|
||||
elements: {}
|
||||
},
|
||||
{%- endfor -%}
|
||||
],
|
||||
},
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endautoescape %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro display_timeline(token, events, origin) %}
|
||||
{% import _self as helper %}
|
||||
<div class="sf-profiler-timeline">
|
||||
<div id="legend-{{ token }}" class="legends"></div>
|
||||
<svg id="timeline-{{ token }}" class="timeline-graph"></svg>
|
||||
<script>{% autoescape 'js' %}
|
||||
window.addEventListener('load', function onLoad() {
|
||||
const theme = new Theme();
|
||||
|
||||
new TimelineEngine(
|
||||
theme,
|
||||
new SvgRenderer(document.getElementById('timeline-{{ token }}')),
|
||||
new Legend(document.getElementById('legend-{{ token }}'), theme),
|
||||
document.getElementById('threshold'),
|
||||
{{ helper.dump_request_data(token, events, origin) }}
|
||||
);
|
||||
});
|
||||
{% endautoescape %}</script>
|
||||
</div>
|
||||
{% endmacro %}
|
457
vendor/symfony/web-profiler-bundle/Resources/views/Collector/time.js
vendored
Normal file
457
vendor/symfony/web-profiler-bundle/Resources/views/Collector/time.js
vendored
Normal file
@ -0,0 +1,457 @@
|
||||
'use strict';
|
||||
|
||||
class TimelineEngine {
|
||||
/**
|
||||
* @param {Theme} theme
|
||||
* @param {Renderer} renderer
|
||||
* @param {Legend} legend
|
||||
* @param {Element} threshold
|
||||
* @param {Object} request
|
||||
* @param {Number} eventHeight
|
||||
* @param {Number} horizontalMargin
|
||||
*/
|
||||
constructor(theme, renderer, legend, threshold, request, eventHeight = 36, horizontalMargin = 10) {
|
||||
this.theme = theme;
|
||||
this.renderer = renderer;
|
||||
this.legend = legend;
|
||||
this.threshold = threshold;
|
||||
this.request = request;
|
||||
this.scale = renderer.width / request.end;
|
||||
this.eventHeight = eventHeight;
|
||||
this.horizontalMargin = horizontalMargin;
|
||||
this.labelY = Math.round(this.eventHeight * 0.48);
|
||||
this.periodY = Math.round(this.eventHeight * 0.66);
|
||||
this.FqcnMatcher = /\\([^\\]+)$/i;
|
||||
this.origin = null;
|
||||
|
||||
this.createEventElements = this.createEventElements.bind(this);
|
||||
this.createBackground = this.createBackground.bind(this);
|
||||
this.createPeriod = this.createPeriod.bind(this);
|
||||
this.render = this.render.bind(this);
|
||||
this.renderEvent = this.renderEvent.bind(this);
|
||||
this.renderPeriod = this.renderPeriod.bind(this);
|
||||
this.onResize = this.onResize.bind(this);
|
||||
this.isActive = this.isActive.bind(this);
|
||||
|
||||
this.threshold.addEventListener('change', this.render);
|
||||
this.legend.addEventListener('change', this.render);
|
||||
|
||||
window.addEventListener('resize', this.onResize);
|
||||
|
||||
this.createElements();
|
||||
this.render();
|
||||
}
|
||||
|
||||
onResize() {
|
||||
this.renderer.measure();
|
||||
this.setScale(this.renderer.width / this.request.end);
|
||||
}
|
||||
|
||||
setScale(scale) {
|
||||
if (scale !== this.scale) {
|
||||
this.scale = scale;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
||||
createElements() {
|
||||
this.origin = this.renderer.setFullVerticalLine(this.createBorder(), 0);
|
||||
this.renderer.add(this.origin);
|
||||
|
||||
this.request.events
|
||||
.filter(event => event.category === 'section')
|
||||
.map(this.createBackground)
|
||||
.forEach(this.renderer.add);
|
||||
|
||||
this.request.events
|
||||
.map(this.createEventElements)
|
||||
.forEach(this.renderer.add);
|
||||
}
|
||||
|
||||
createBackground(event) {
|
||||
const subrequest = event.name === '__section__.child';
|
||||
const background = this.renderer.create('rect', subrequest ? 'timeline-subrequest' : 'timeline-border');
|
||||
|
||||
event.elements = Object.assign(event.elements || {}, { background });
|
||||
|
||||
return background;
|
||||
}
|
||||
|
||||
createEventElements(event) {
|
||||
const { name, category, duration, memory, periods } = event;
|
||||
const border = this.renderer.setFullHorizontalLine(this.createBorder(), 0);
|
||||
const lines = periods.map(period => this.createPeriod(period, category));
|
||||
const label = this.createLabel(this.getShortName(name), duration, memory, periods[0]);
|
||||
const title = this.renderer.createTitle(name);
|
||||
const group = this.renderer.group([title, border, label].concat(lines), this.theme.getCategoryColor(event.category));
|
||||
|
||||
event.elements = Object.assign(event.elements || {}, { group, label, border });
|
||||
|
||||
this.legend.add(event.category)
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
createLabel(name, duration, memory, period) {
|
||||
const label = this.renderer.createText(name, period.start * this.scale, this.labelY, 'timeline-label');
|
||||
const sublabel = this.renderer.createTspan(` ${duration} ms / ${memory} MiB`, 'timeline-sublabel');
|
||||
|
||||
label.appendChild(sublabel);
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
createPeriod(period, category) {
|
||||
const timeline = this.renderer.createPath(null, 'timeline-period', this.theme.getCategoryColor(category));
|
||||
|
||||
period.draw = category === 'section' ? this.renderer.setSectionLine : this.renderer.setPeriodLine;
|
||||
period.elements = Object.assign(period.elements || {}, { timeline });
|
||||
|
||||
return timeline;
|
||||
}
|
||||
|
||||
createBorder() {
|
||||
return this.renderer.createPath(null, 'timeline-border');
|
||||
}
|
||||
|
||||
isActive(event) {
|
||||
const { duration, category } = event;
|
||||
|
||||
return duration >= this.threshold.value && this.legend.isActive(category);
|
||||
}
|
||||
|
||||
render() {
|
||||
const events = this.request.events.filter(this.isActive);
|
||||
const width = this.renderer.width + this.horizontalMargin * 2;
|
||||
const height = this.eventHeight * events.length;
|
||||
|
||||
// Set view box
|
||||
this.renderer.setViewBox(-this.horizontalMargin, 0, width, height);
|
||||
|
||||
// Show 0ms origin
|
||||
this.renderer.setFullVerticalLine(this.origin, 0);
|
||||
|
||||
// Render all events
|
||||
this.request.events.forEach(event => this.renderEvent(event, events.indexOf(event)));
|
||||
}
|
||||
|
||||
renderEvent(event, index) {
|
||||
const { name, category, duration, memory, periods, elements } = event;
|
||||
const { group, label, border, background } = elements;
|
||||
const visible = index >= 0;
|
||||
|
||||
group.setAttribute('visibility', visible ? 'visible' : 'hidden');
|
||||
|
||||
if (background) {
|
||||
background.setAttribute('visibility', visible ? 'visible' : 'hidden');
|
||||
|
||||
if (visible) {
|
||||
const [min, max] = this.getEventLimits(event);
|
||||
|
||||
this.renderer.setFullRectangle(background, min * this.scale, max * this.scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
// Position the group
|
||||
group.setAttribute('transform', `translate(0, ${index * this.eventHeight})`);
|
||||
|
||||
// Update top border
|
||||
this.renderer.setFullHorizontalLine(border, 0);
|
||||
|
||||
// render label and ensure it doesn't escape the viewport
|
||||
this.renderLabel(label, event);
|
||||
|
||||
// Update periods
|
||||
periods.forEach(this.renderPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
renderLabel(label, event) {
|
||||
const width = this.getLabelWidth(label);
|
||||
const [min, max] = this.getEventLimits(event);
|
||||
const alignLeft = (min * this.scale) + width <= this.renderer.width;
|
||||
|
||||
label.setAttribute('x', (alignLeft ? min : max) * this.scale);
|
||||
label.setAttribute('text-anchor', alignLeft ? 'start' : 'end');
|
||||
}
|
||||
|
||||
renderPeriod(period) {
|
||||
const { elements, start, duration } = period;
|
||||
|
||||
period.draw(elements.timeline, start * this.scale, this.periodY, Math.max(duration * this.scale, 1));
|
||||
}
|
||||
|
||||
getLabelWidth(label) {
|
||||
if (typeof label.width === 'undefined') {
|
||||
label.width = label.getBBox().width;
|
||||
}
|
||||
|
||||
return label.width;
|
||||
}
|
||||
|
||||
getEventLimits(event) {
|
||||
if (typeof event.limits === 'undefined') {
|
||||
const { periods } = event;
|
||||
|
||||
event.limits = [
|
||||
periods[0].start,
|
||||
periods[periods.length - 1].end
|
||||
];
|
||||
}
|
||||
|
||||
return event.limits;
|
||||
}
|
||||
|
||||
getShortName(name) {
|
||||
const matches = this.FqcnMatcher.exec(name);
|
||||
|
||||
if (matches) {
|
||||
return matches[1];
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
class Legend {
|
||||
constructor(element, theme) {
|
||||
this.element = element;
|
||||
this.theme = theme;
|
||||
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.createCategory = this.createCategory.bind(this);
|
||||
|
||||
this.categories = [];
|
||||
this.theme.getDefaultCategories().forEach(this.createCategory);
|
||||
}
|
||||
|
||||
add(category) {
|
||||
this.get(category).classList.add('present');
|
||||
}
|
||||
|
||||
createCategory(category) {
|
||||
const element = document.createElement('button');
|
||||
element.className = `timeline-category active`;
|
||||
element.style.borderColor = this.theme.getCategoryColor(category);
|
||||
element.innerText = category;
|
||||
element.value = category;
|
||||
element.type = 'button';
|
||||
element.addEventListener('click', this.toggle);
|
||||
|
||||
this.element.appendChild(element);
|
||||
|
||||
this.categories.push(element);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
toggle(event) {
|
||||
event.target.classList.toggle('active');
|
||||
|
||||
this.emit('change');
|
||||
}
|
||||
|
||||
isActive(category) {
|
||||
return this.get(category).classList.contains('active');
|
||||
}
|
||||
|
||||
get(category) {
|
||||
return this.categories.find(element => element.value === category) || this.createCategory(category);
|
||||
}
|
||||
|
||||
emit(name) {
|
||||
this.element.dispatchEvent(new Event(name));
|
||||
}
|
||||
|
||||
addEventListener(name, callback) {
|
||||
this.element.addEventListener(name, callback);
|
||||
}
|
||||
|
||||
removeEventListener(name, callback) {
|
||||
this.element.removeEventListener(name, callback);
|
||||
}
|
||||
}
|
||||
|
||||
class SvgRenderer {
|
||||
/**
|
||||
* @param {SVGElement} element
|
||||
*/
|
||||
constructor(element) {
|
||||
this.ns = 'http://www.w3.org/2000/svg';
|
||||
this.width = null;
|
||||
this.viewBox = {};
|
||||
this.element = element;
|
||||
|
||||
this.add = this.add.bind(this);
|
||||
|
||||
this.setViewBox(0, 0, 0, 0);
|
||||
this.measure();
|
||||
}
|
||||
|
||||
setViewBox(x, y, width, height) {
|
||||
this.viewBox = { x, y, width, height };
|
||||
this.element.setAttribute('viewBox', `${x} ${y} ${width} ${height}`);
|
||||
}
|
||||
|
||||
measure() {
|
||||
this.width = this.element.getBoundingClientRect().width;
|
||||
}
|
||||
|
||||
add(element) {
|
||||
this.element.appendChild(element);
|
||||
}
|
||||
|
||||
group(elements, className) {
|
||||
const group = this.create('g', className);
|
||||
|
||||
elements.forEach(element => group.appendChild(element));
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
setHorizontalLine(element, x, y, width) {
|
||||
element.setAttribute('d', `M${x},${y} h${width}`);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
setVerticalLine(element, x, y, height) {
|
||||
element.setAttribute('d', `M${x},${y} v${height}`);
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
setFullHorizontalLine(element, y) {
|
||||
return this.setHorizontalLine(element, this.viewBox.x, y, this.viewBox.width);
|
||||
}
|
||||
|
||||
setFullVerticalLine(element, x) {
|
||||
return this.setVerticalLine(element, x, this.viewBox.y, this.viewBox.height);
|
||||
}
|
||||
|
||||
setFullRectangle(element, min, max) {
|
||||
element.setAttribute('x', min);
|
||||
element.setAttribute('y', this.viewBox.y);
|
||||
element.setAttribute('width', max - min);
|
||||
element.setAttribute('height', this.viewBox.height);
|
||||
}
|
||||
|
||||
setSectionLine(element, x, y, width, height = 4, markerSize = 6) {
|
||||
const totalHeight = height + markerSize;
|
||||
const maxMarkerWidth = Math.min(markerSize, width / 2);
|
||||
const widthWithoutMarker = Math.max(0, width - (maxMarkerWidth * 2));
|
||||
|
||||
element.setAttribute('d', `M${x},${y + totalHeight} v${-totalHeight} h${width} v${totalHeight} l${-maxMarkerWidth} ${-markerSize} h${-widthWithoutMarker} Z`);
|
||||
}
|
||||
|
||||
setPeriodLine(element, x, y, width, height = 4, markerWidth = 2, markerHeight = 4) {
|
||||
const totalHeight = height + markerHeight;
|
||||
const maxMarkerWidth = Math.min(markerWidth, width);
|
||||
|
||||
element.setAttribute('d', `M${x + maxMarkerWidth},${y + totalHeight} h${-maxMarkerWidth} v${-totalHeight} h${width} v${height} h${maxMarkerWidth-width}Z`);
|
||||
}
|
||||
|
||||
createText(content, x, y, className) {
|
||||
const element = this.create('text', className);
|
||||
|
||||
element.setAttribute('x', x);
|
||||
element.setAttribute('y', y);
|
||||
element.textContent = content;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createTspan(content, className) {
|
||||
const element = this.create('tspan', className);
|
||||
|
||||
element.textContent = content;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createTitle(content) {
|
||||
const element = this.create('title');
|
||||
|
||||
element.textContent = content;
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
createPath(path = null, className = null, color = null) {
|
||||
const element = this.create('path', className);
|
||||
|
||||
if (path) {
|
||||
element.setAttribute('d', path);
|
||||
}
|
||||
|
||||
if (color) {
|
||||
element.setAttribute('fill', color);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
create(name, className = null) {
|
||||
const element = document.createElementNS(this.ns, name);
|
||||
|
||||
if (className) {
|
||||
element.setAttribute('class', className);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
}
|
||||
|
||||
class Theme {
|
||||
constructor(element) {
|
||||
this.reservedCategoryColors = {
|
||||
'default': '#777',
|
||||
'section': '#999',
|
||||
'event_listener': '#00b8f5',
|
||||
'template': '#66cc00',
|
||||
'doctrine': '#ff6633',
|
||||
'messenger_middleware': '#bdb81e',
|
||||
'controller.argument_value_resolver': '#8c5de6',
|
||||
'http_client': '#ffa333',
|
||||
};
|
||||
|
||||
this.customCategoryColors = [
|
||||
'#dbab09', // dark yellow
|
||||
'#ea4aaa', // pink
|
||||
'#964b00', // brown
|
||||
'#22863a', // dark green
|
||||
'#0366d6', // dark blue
|
||||
'#17a2b8', // teal
|
||||
];
|
||||
|
||||
this.getCategoryColor = this.getCategoryColor.bind(this);
|
||||
this.getDefaultCategories = this.getDefaultCategories.bind(this);
|
||||
}
|
||||
|
||||
getDefaultCategories() {
|
||||
return Object.keys(this.reservedCategoryColors);
|
||||
}
|
||||
|
||||
getCategoryColor(category) {
|
||||
return this.reservedCategoryColors[category] || this.getRandomColor(category);
|
||||
}
|
||||
|
||||
getRandomColor(category) {
|
||||
// instead of pure randomness, colors are assigned deterministically based on the
|
||||
// category name, to ensure that each custom category always displays the same color
|
||||
return this.customCategoryColors[this.hash(category) % this.customCategoryColors.length];
|
||||
}
|
||||
|
||||
// copied from https://github.com/darkskyapp/string-hash
|
||||
hash(string) {
|
||||
var hash = 5381;
|
||||
var i = string.length;
|
||||
|
||||
while(i) {
|
||||
hash = (hash * 33) ^ string.charCodeAt(--i);
|
||||
}
|
||||
|
||||
return hash >>> 0;
|
||||
}
|
||||
}
|
210
vendor/symfony/web-profiler-bundle/Resources/views/Collector/translation.html.twig
vendored
Normal file
210
vendor/symfony/web-profiler-bundle/Resources/views/Collector/translation.html.twig
vendored
Normal file
@ -0,0 +1,210 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% import _self as helper %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.messages|length %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/translation.svg') }}
|
||||
{% set status_color = collector.countMissings ? 'red' : collector.countFallbacks ? 'yellow' %}
|
||||
{% set error_count = collector.countMissings + collector.countFallbacks %}
|
||||
<span class="sf-toolbar-value">{{ error_count ?: collector.countDefines }}</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Default locale</b>
|
||||
<span class="sf-toolbar-status">
|
||||
{{ collector.locale|default('-') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Missing messages</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countMissings ? 'red' }}">
|
||||
{{ collector.countMissings }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Fallback messages</b>
|
||||
<span class="sf-toolbar-status sf-toolbar-status-{{ collector.countFallbacks ? 'yellow' }}">
|
||||
{{ collector.countFallbacks }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Defined messages</b>
|
||||
<span class="sf-toolbar-status">{{ collector.countDefines }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label label-status-{{ collector.countMissings ? 'error' : collector.countFallbacks ? 'warning' }} {{ collector.messages is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/translation.svg') }}</span>
|
||||
<strong>Translation</strong>
|
||||
{% if collector.countMissings or collector.countFallbacks %}
|
||||
{% set error_count = collector.countMissings + collector.countFallbacks %}
|
||||
<span class="count">
|
||||
<span>{{ error_count }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Translation</h2>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.locale|default('-') }}</span>
|
||||
<span class="label">Default locale</span>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.fallbackLocales|join(', ')|default('-') }}</span>
|
||||
<span class="label">Fallback locale{{ collector.fallbackLocales|length != 1 ? 's' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Messages</h2>
|
||||
|
||||
{% if collector.messages is empty %}
|
||||
<div class="empty">
|
||||
<p>No translations have been called.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% block messages %}
|
||||
|
||||
{# sort translation messages in groups #}
|
||||
{% set messages_defined, messages_missing, messages_fallback = [], [], [] %}
|
||||
{% for message in collector.messages %}
|
||||
{% if message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_DEFINED') %}
|
||||
{% set messages_defined = messages_defined|merge([message]) %}
|
||||
{% elseif message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_MISSING') %}
|
||||
{% set messages_missing = messages_missing|merge([message]) %}
|
||||
{% elseif message.state == constant('Symfony\\Component\\Translation\\DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK') %}
|
||||
{% set messages_fallback = messages_fallback|merge([message]) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="sf-tabs">
|
||||
<div class="tab {{ collector.countMissings == 0 ? 'active' }}">
|
||||
<h3 class="tab-title">Defined <span class="badge">{{ collector.countDefines }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<p class="help">
|
||||
These messages are correctly translated into the given locale.
|
||||
</p>
|
||||
|
||||
{% if messages_defined is empty %}
|
||||
<div class="empty">
|
||||
<p>None of the used translation messages are defined for the given locale.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% block defined_messages %}
|
||||
{{ helper.render_table(messages_defined) }}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab">
|
||||
<h3 class="tab-title">Fallback <span class="badge {{ collector.countFallbacks ? 'status-warning' }}">{{ collector.countFallbacks }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<p class="help">
|
||||
These messages are not available for the given locale
|
||||
but Symfony found them in the fallback locale catalog.
|
||||
</p>
|
||||
|
||||
{% if messages_fallback is empty %}
|
||||
<div class="empty">
|
||||
<p>No fallback translation messages were used.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% block fallback_messages %}
|
||||
{{ helper.render_table(messages_fallback, true) }}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab {{ collector.countMissings > 0 ? 'active' }}">
|
||||
<h3 class="tab-title">Missing <span class="badge {{ collector.countMissings ? 'status-error' }}">{{ collector.countMissings }}</span></h3>
|
||||
|
||||
<div class="tab-content">
|
||||
<p class="help">
|
||||
These messages are not available for the given locale and cannot
|
||||
be found in the fallback locales. Add them to the translation
|
||||
catalogue to avoid Symfony outputting untranslated contents.
|
||||
</p>
|
||||
|
||||
{% if messages_missing is empty %}
|
||||
<div class="empty">
|
||||
<p>There are no messages of this category.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% block missing_messages %}
|
||||
{{ helper.render_table(messages_missing) }}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>Sfjs.createFilters();</script>
|
||||
|
||||
{% endblock messages %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% macro render_table(messages, is_fallback) %}
|
||||
<table data-filters>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-filter="locale">Locale</th>
|
||||
{% if is_fallback %}
|
||||
<th>Fallback locale</th>
|
||||
{% endif %}
|
||||
<th data-filter="domain">Domain</th>
|
||||
<th>Times used</th>
|
||||
<th>Message ID</th>
|
||||
<th>Message Preview</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for message in messages %}
|
||||
<tr data-filter-locale="{{ message.locale }}" data-filter-domain="{{ message.domain }}">
|
||||
<td class="font-normal text-small nowrap">{{ message.locale }}</td>
|
||||
{% if is_fallback %}
|
||||
<td class="font-normal text-small nowrap">{{ message.fallbackLocale|default('-') }}</td>
|
||||
{% endif %}
|
||||
<td class="font-normal text-small text-bold nowrap">{{ message.domain }}</td>
|
||||
<td class="font-normal text-small nowrap">{{ message.count }}</td>
|
||||
<td>
|
||||
<span class="nowrap">{{ message.id }}</span>
|
||||
|
||||
{% if message.transChoiceNumber is not null %}
|
||||
<small class="newline">(pluralization is used)</small>
|
||||
{% endif %}
|
||||
|
||||
{% if message.parameters|length > 0 %}
|
||||
<button class="btn-link newline text-small sf-toggle" data-toggle-selector="#parameters-{{ loop.index }}" data-toggle-alt-content="Hide parameters">Show parameters</button>
|
||||
|
||||
<div id="parameters-{{ loop.index }}" class="hidden">
|
||||
{% for parameters in message.parameters %}
|
||||
{{ profiler_dump(parameters, maxDepth=1) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="prewrap">{{ message.translation }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endmacro %}
|
115
vendor/symfony/web-profiler-bundle/Resources/views/Collector/twig.html.twig
vendored
Normal file
115
vendor/symfony/web-profiler-bundle/Resources/views/Collector/twig.html.twig
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% set time = collector.templatecount ? '%0.0f'|format(collector.time) : 'n/a' %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/twig.svg') }}
|
||||
<span class="sf-toolbar-value">{{ time }}</span>
|
||||
<span class="sf-toolbar-label">ms</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Render Time</b>
|
||||
<span>{{ time }} ms</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Template Calls</b>
|
||||
<span class="sf-toolbar-status">{{ collector.templatecount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Block Calls</b>
|
||||
<span class="sf-toolbar-status">{{ collector.blockcount }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Macro Calls</b>
|
||||
<span class="sf-toolbar-status">{{ collector.macrocount }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url }) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{ 0 == collector.templateCount ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/twig.svg') }}</span>
|
||||
<strong>Twig</strong>
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
{% if collector.templatecount == 0 %}
|
||||
<h2>Twig</h2>
|
||||
|
||||
<div class="empty">
|
||||
<p>No Twig templates were rendered for this request.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<h2>Twig Metrics</h2>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<span class="value">{{ '%0.0f'|format(collector.time) }} <span class="unit">ms</span></span>
|
||||
<span class="label">Render time</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.templatecount }}</span>
|
||||
<span class="label">Template calls</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.blockcount }}</span>
|
||||
<span class="label">Block calls</span>
|
||||
</div>
|
||||
|
||||
<div class="metric">
|
||||
<span class="value">{{ collector.macrocount }}</span>
|
||||
<span class="label">Macro calls</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="help">
|
||||
Render time includes sub-requests rendering time (if any).
|
||||
</p>
|
||||
|
||||
<h2>Rendered Templates</h2>
|
||||
|
||||
<table id="twig-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Template Name & Path</th>
|
||||
<th class="num-col" scope="col">Render Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for template, count in collector.templates %}
|
||||
<tr>
|
||||
{%- set file = collector.templatePaths[template]|default(false) -%}
|
||||
{%- set link = file ? file|file_link(1) : false -%}
|
||||
<td>
|
||||
<span class="sf-icon icon-twig">{{ include('@WebProfiler/Icon/twig.svg') }}</span>
|
||||
{% if link %}
|
||||
<a href="{{ link }}" title="{{ file }}">{{ template }}</a>
|
||||
<div>
|
||||
<a class="text-muted" href="{{ link }}" title="{{ file }}">
|
||||
{{ file|file_relative|default(file) }}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
{{ template }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="font-normal num-col">{{ count }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Rendering Call Graph</h2>
|
||||
|
||||
<div id="twig-dump">
|
||||
{{ collector.htmlcallgraph }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
103
vendor/symfony/web-profiler-bundle/Resources/views/Collector/validator.html.twig
vendored
Normal file
103
vendor/symfony/web-profiler-bundle/Resources/views/Collector/validator.html.twig
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
|
||||
|
||||
{% block toolbar %}
|
||||
{% if collector.violationsCount > 0 or collector.calls|length %}
|
||||
{% set status_color = collector.violationsCount ? 'red' %}
|
||||
{% set icon %}
|
||||
{{ include('@WebProfiler/Icon/validator.svg') }}
|
||||
<span class="sf-toolbar-value">
|
||||
{{ collector.violationsCount ?: collector.calls|length }}
|
||||
</span>
|
||||
{% endset %}
|
||||
|
||||
{% set text %}
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Validator calls</b>
|
||||
<span class="sf-toolbar-status">{{ collector.calls|length }}</span>
|
||||
</div>
|
||||
<div class="sf-toolbar-info-piece">
|
||||
<b>Number of violations</b>
|
||||
<span class="sf-toolbar-status {{- collector.violationsCount > 0 ? ' sf-toolbar-status-red' }}">{{ collector.violationsCount }}</span>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block menu %}
|
||||
<span class="label {{- collector.violationsCount ? ' label-status-error' }} {{ collector.calls is empty ? 'disabled' }}">
|
||||
<span class="icon">{{ include('@WebProfiler/Icon/validator.svg') }}</span>
|
||||
<strong>Validator</strong>
|
||||
{% if collector.violationsCount > 0 %}
|
||||
<span class="count">
|
||||
<span>{{ collector.violationsCount }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
<h2>Validator calls</h2>
|
||||
|
||||
{% for call in collector.calls %}
|
||||
<div class="sf-validator sf-reset">
|
||||
<span class="metadata">In
|
||||
{% set caller = call.caller %}
|
||||
{% if caller.line %}
|
||||
{% set link = caller.file|file_link(caller.line) %}
|
||||
{% if link %}
|
||||
<a href="{{ link }}" title="{{ caller.file }}">{{ caller.name }}</a>
|
||||
{% else %}
|
||||
<abbr title="{{ caller.file }}">{{ caller.name }}</abbr>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ caller.name }}
|
||||
{% endif %}
|
||||
line <a class="text-small sf-toggle" data-toggle-selector="#sf-trace-{{ loop.index0 }}">{{ caller.line }}</a> (<a class="text-small sf-toggle" data-toggle-selector="#sf-context-{{ loop.index0 }}">context</a>):
|
||||
</span>
|
||||
|
||||
<div class="sf-validator-compact hidden" id="sf-trace-{{ loop.index0 }}">
|
||||
<div class="trace">
|
||||
{{ caller.file|file_excerpt(caller.line)|replace({
|
||||
'#DD0000': 'var(--highlight-string)',
|
||||
'#007700': 'var(--highlight-keyword)',
|
||||
'#0000BB': 'var(--highlight-default)',
|
||||
'#FF8000': 'var(--highlight-comment)'
|
||||
})|raw }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sf-validator-compact hidden sf-validator-context" id="sf-context-{{ loop.index0 }}">
|
||||
{{ profiler_dump(call.context, maxDepth=1) }}
|
||||
</div>
|
||||
|
||||
{% if call.violations|length %}
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Path</th>
|
||||
<th>Message</th>
|
||||
<th>Invalid value</th>
|
||||
<th>Violation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for violation in call.violations %}
|
||||
<tr>
|
||||
<td>{{ violation.propertyPath }}</td>
|
||||
<td>{{ violation.message }}</td>
|
||||
<td>{{ profiler_dump(violation.seek('invalidValue')) }}</td>
|
||||
<td>{{ profiler_dump(violation) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
No violations
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty">
|
||||
<p>No calls to the validator were collected during this request.</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
Reference in New Issue
Block a user