{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var entities \EasyCorp\Bundle\EasyAdminBundle\Collection\EntityDtoCollection #}
{# @var paginator \EasyCorp\Bundle\EasyAdminBundle\Orm\EntityPaginator #}
{% extends ea.templatePath('layout') %}
{% trans_default_domain ea.i18n.translationDomain %}
{# TODO: change this when reenabling batch actions #}
{% set has_batch_actions = false and batch_form is defined and ea.crud.actions.batchActions|length > 0 %}
{% block body_id entities|length > 0 ? 'ea-index-' ~ entities|first.name : '' %}
{% block body_class 'index' ~ (entities|length > 0 ? ' index-' ~ entities|first.name : '') %}
{% block content_title %}
{%- apply spaceless -%}
{% set default_title = ea.crud.defaultPageTitle('index')|trans(ea.i18n.translationParameters, 'EasyAdminBundle') %}
{{ ea.crud.customPageTitle is null ? default_title|raw : ea.crud.customPageTitle('index')|trans(ea.i18n.translationParameters)|raw }}
{%- endapply -%}
{% endblock %}
{% block page_actions %}
{% block global_actions %}
{% for action in global_actions %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
{% endblock global_actions %}
{% endblock page_actions %}
{% block content_header %}
{{ parent() }}
{# TODO:
{% block batch_actions %}
{% for batchAction in ea.crud.actions.batchActions %}
{{ include(batchAction.templatePath, { action: batchAction }, with_context = false) }}
{% endfor %}
{{ include('@EasyAdmin/crud/includes/_batch_action_modal.html.twig', {}, with_context = false) }}
{% endblock %}
#}
{% endblock content_header %}
{% block main %}
{# sort can be multiple; let's consider the sorting field the first one #}
{% set sort_field_name = app.request.get('sort')|keys|first %}
{% set sort_order = app.request.get('sort')|first %}
{% set some_results_are_hidden = false %}
{% set has_footer = entities|length != 0 %}
{% set has_search = ea.crud.isSearchEnabled %}
{% set has_filters = filters|length > 0 %}
{% set has_datagrid_tools = has_search or has_filters %}
{% if has_datagrid_tools %}
<div class="datagrid-header-tools">
<div class="datagrid-search">
{% block search %}
{% if has_search %}
<div class="form-action form-action-search">
<form method="get">
{% block search_form %}
{# reset the referrer and page number whenever a new query is performed #}
{% set query_parameters = ea.request.query.all|merge({
referrer: null, page: 1,
}) %}
{# browsers remove the query string when submitting forms using GET;
that's why all query string parameters are added as hidden form fields #}
{% for paramName, paramValue in query_parameters|ea_flatten_array %}
<input type="hidden" name="{{ paramName }}" value="{{ paramValue }}">
{% endfor %}
<div class="form-group">
<div class="form-widget">
<input class="form-control" type="search" name="query" value="{{ app.request.get('query') ?? '' }}" placeholder="{{ 'action.search'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}">
</div>
</div>
{% endblock %}
</form>
</div>
{% endif %}
{% endblock search %}
</div>
<div class="datagrid-filters">
{% block filters %}
{% if filters|length > 0 %}
{% set applied_filters = ea.request.query.all['filters']|default([])|keys %}
<div class="btn-group action-filters">
<a href="{{ ea_url().setAction('renderFilters').includeReferrer() }}" class="btn btn-secondary btn-labeled btn-labeled-right action-filters-button {{ applied_filters ? 'action-filters-applied' }}" data-modal="#modal-filters">
<i class="fa fa-filter fa-fw"></i> {{ 'filter.title'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}{% if applied_filters %} <span class="text-primary">({{ applied_filters|length }})</span>{% endif %}
</a>
{% if applied_filters %}
<a href="{{ ea_url().unset('filters') }}" class="btn btn-secondary action-filters-reset">
<i class="fa fa-close"></i>
</a>
{% endif %}
</div>
{% endif %}
{% endblock filters %}
</div>
</div>
{% endif %}
<div class="content-panel">
<div class="content-panel-body with-rounded-top with-min-h-250 without-padding {{ not has_footer ? 'without-footer' }}">
<table class="table datagrid with-rounded-top {{ not has_footer ? 'with-rounded-bottom' }}">
<thead>
{% block table_head %}
<tr>
{% if has_batch_actions %}
<th width="1px"><span><input type="checkbox" class="form-batch-checkbox-all"></span></th>
{% endif %}
{% set ea_sort_asc = constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Option\\SortOrder::ASC') %}
{% set ea_sort_desc = constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Option\\SortOrder::DESC') %}
{% for field in entities|first.fields ?? [] %}
{% set is_sorting_field = ea.search.isSortingField(field.property) %}
{% set next_sort_direction = is_sorting_field ? (ea.search.sortDirection(field.property) == ea_sort_desc ? ea_sort_asc : ea_sort_desc) : ea_sort_desc %}
{% set column_icon = is_sorting_field ? (next_sort_direction == ea_sort_desc ? 'fa-arrow-up' : 'fa-arrow-down') : 'fa-sort' %}
<th class="{{ is_sorting_field ? 'sorted' }} {{ field.isVirtual ? 'field-virtual' }} text-{{ field.textAlign }}" dir="{{ ea.i18n.textDirection }}">
{% if field.isSortable %}
<a href="{{ ea_url({ page: 1, sort: { (field.property): next_sort_direction } }).includeReferrer() }}">
{{ field.label|raw }} <i class="fa fa-fw {{ column_icon }}"></i>
</a>
{% else %}
<span>{{ field.label|raw }}</span>
{% endif %}
</th>
{% endfor %}
<th {% if ea.crud.showEntityActionsAsDropdown %}width="10px"{% endif %} dir="{{ ea.i18n.textDirection }}">
<span class="sr-only">{{ 'action.entity_actions'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}</span>
</th>
</tr>
{% endblock table_head %}
</thead>
<tbody>
{% block table_body %}
{% for entity in entities %}
{% if not entity.isAccessible %}
{% set some_results_are_hidden = true %}
{% else %}
<tr data-id="{{ entity.primaryKeyValueAsString }}">
{% if has_batch_actions %}
<td><input type="checkbox" class="form-batch-checkbox" value="{{ entity.primaryKeyValue }}"></td>
{% endif %}
{% for field in entity.fields %}
<td class="{{ field.property == sort_field_name ? 'sorted' }} text-{{ field.textAlign }} {{ field.cssClass }}" dir="{{ ea.i18n.textDirection }}">
{{ include(field.templatePath, { field: field, entity: entity }, with_context = false) }}
</td>
{% endfor %}
{% block entity_actions %}
<td class="actions">
{% if not ea.crud.showEntityActionsAsDropdown %}
{% for action in entity.actions %}
{{ include(action.templatePath, { action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }}
{% endfor %}
{% else %}
<div class="dropdown dropdown-actions">
<a class="dropdown-toggle btn btn-secondary btn-sm" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-fw fa-ellipsis-h"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
{% for action in entity.actions %}
{{ include(action.templatePath, { action: action, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }}
{% endfor %}
</div>
</div>
{% endif %}
</td>
{% endblock entity_actions %}
</tr>
{% endif %}
{% else %}
<tr>
<td class="no-results" colspan="100">
{{ 'datagrid.no_results'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}
</td>
</tr>
{% endfor %}
{% if some_results_are_hidden %}
<tr class="datagrid-row-empty">
<td class="text-center" colspan="{{ entities|first.fields|length + 1 }}">
<span class="datagrid-row-empty-message"><i class="fa fa-lock mr-1"></i> {{ 'datagrid.hidden_results'|trans({}, 'EasyAdminBundle') }}</span>
</td>
</tr>
{% endif %}
{% endblock table_body %}
</tbody>
</table>
</div>
{% if entities|length > 0 %}
<div class="content-panel-footer without-padding without-border">
{% block paginator %}
{{ include(ea.templatePath('crud/paginator')) }}
{% endblock paginator %}
</div>
{% endif %}
</div>
{% block delete_form %}
{{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', with_context = false) }}
{% endblock delete_form %}
{% if filters|length > 0 %}
{{ include('@EasyAdmin/crud/includes/_filters_modal.html.twig') }}
{% endif %}
{% endblock main %}
{% block body_javascript %}
{{ parent() }}
<script type="text/javascript">
$(function() {
const customSwitches = document.querySelectorAll('td.field-boolean .custom-control.custom-switch input[type="checkbox"]');
for (i = 0; i < customSwitches.length; i++) {
customSwitches[i].addEventListener('change', function () {
const customSwitch = this;
const newValue = this.checked;
const oldValue = !newValue;
const fieldName = this.closest('.custom-switch').dataset.fieldname;
const toggleUrl = "{{ ea_url().setAction('edit')|raw }}"
+ "&entityId=" + this.closest('[data-id]').dataset.id
+ "&fieldName=" + fieldName
+ "&newValue=" + newValue.toString();
let toggleRequest = $.ajax({ type: "GET", url: toggleUrl, data: {} });
toggleRequest.done(function(result) {});
toggleRequest.fail(function() {
// in case of error, restore the original value and disable the toggle
customSwitch.checked = oldValue;
customSwitch.disabled = true;
customSwitch.closest('.custom-switch').classList.add('disabled');
});
});
}
$('.action-delete').on('click', function(e) {
e.preventDefault();
const id = $(this).parents('[data-id]').first().data('id');
$('#modal-delete').modal({ backdrop: true, keyboard: true })
.off('click', '#modal-delete-button')
.on('click', '#modal-delete-button', function () {
let deleteForm = $('#delete-form');
deleteForm.attr('action', deleteForm.attr('action').replace('__entityId_placeholder__', id));
deleteForm.trigger('submit');
});
});
{% if filters|length > 0 %}
// HTML5 specifies that a <script> tag inserted with innerHTML should not execute
// https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#Security_considerations
// That's why we can't use just 'innerHTML'. See https://stackoverflow.com/a/47614491/2804294
let setInnerHTML = function(element, htmlContent) {
element.innerHTML = htmlContent;
Array.from(element.querySelectorAll('script')).forEach(oldScript => {
const newScript = document.createElement('script');
Array.from(oldScript.attributes)
.forEach(attr => newScript.setAttribute(attr.name, attr.value));
newScript.appendChild(document.createTextNode(oldScript.innerHTML));
oldScript.parentNode.replaceChild(newScript, oldScript);
});
};
document.querySelector('.action-filters-button').addEventListener('click', function(event) {
let filterButton = event.currentTarget;
let filterModal = document.querySelector(filterButton.dataset.modal);
let filterModalBody = filterModal.querySelector('.modal-body');
$(filterModal).modal({ backdrop: true, keyboard: true });
filterModalBody.innerHTML = '<div class="fa-3x px-3 py-3 text-muted text-center"><i class="fas fa-circle-notch fa-spin"></i></div>';
$.get(filterButton.getAttribute('href'), function (response) {
setInnerHTML(filterModalBody, response);
});
event.preventDefault();
event.stopPropagation();
});
{% endif %}
{% if has_batch_actions %}
const titleContent = $('.content-header-title > .title').html();
$(document).on('click', '.deselect-batch-button', function () {
$(this).closest('.content').find(':checkbox.form-batch-checkbox-all').prop('checked', false).trigger('change');
});
$(document).on('change', '.form-batch-checkbox-all', function () {
$(this).closest('.content').find(':checkbox.form-batch-checkbox').prop('checked', $(this).prop('checked')).trigger('change');
});
$(document).on('change', '.form-batch-checkbox', function () {
const $content = $(this).closest('.content');
let $input = $content.find(':hidden#batch_form_ids');
let ids = $input.val() ? $input.val().split(',') : [];
const id = $(this).val();
if ($(this).prop('checked')) {
if (-1 === ids.indexOf(id)) {
ids.push(id);
}
} else {
ids = ids.filter(function(value) { return value !== id });
$content.find(':checkbox.form-batch-checkbox-all').prop('checked', false);
}
if (0 === ids.length) {
$content.find('.global-actions').show();
$content.find('.batch-actions').hide();
$content.find('table').removeClass('table-batch');
} else {
$content.find('.batch-actions').show();
$content.find('.global-actions').hide();
$content.find('table').addClass('table-batch');
}
$input.val(ids.join(','));
$content.find('.content-header-title > .title').html(0 === ids.length ? titleContent : '');
});
$('button[name="batch_form[name]"].ask-confirm-batch-button').on('click', function (event) {
event.preventDefault();
event.stopPropagation();
let $button = $(this);
$('#modal-batch-action').modal({ backdrop : true, keyboard : true })
.off('click', '#modal-batch-action-button')
.on('click', '#modal-batch-action-button', function () {
$button.unbind('click');
$button.trigger('click');
});
});
{% endif %}
});
</script>
{% if app.request.get('query') is not empty %}
<script type="text/javascript">
const search_query = "{{ ea.search.query|default('')|e('js') }}";
// the original query is prepended to allow matching exact phrases in addition to single words
$('#main').find('table tbody td:not(.actions)').highlight($.merge([search_query], search_query.split(' ')));
</script>
{% endif %}
{% endblock %}