vendor/easycorp/easyadmin-bundle/src/Resources/views/crud/index.html.twig line 1

Open in your IDE?
  1. {# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
  2. {# @var entities \EasyCorp\Bundle\EasyAdminBundle\Collection\EntityDtoCollection #}
  3. {# @var paginator \EasyCorp\Bundle\EasyAdminBundle\Orm\EntityPaginator #}
  4. {% extends ea.templatePath('layout') %}
  5. {% trans_default_domain ea.i18n.translationDomain %}
  6. {# TODO: change this when reenabling batch actions #}
  7. {% set has_batch_actions = false and batch_form is defined and ea.crud.actions.batchActions|length > 0 %}
  8. {% block body_id entities|length > 0 ? 'ea-index-' ~ entities|first.name : '' %}
  9. {% block body_class 'index' ~ (entities|length > 0 ? ' index-' ~ entities|first.name : '') %}
  10. {% block content_title %}
  11.     {%- apply spaceless -%}
  12.         {% set default_title = ea.crud.defaultPageTitle('index')|trans(ea.i18n.translationParameters, 'EasyAdminBundle') %}
  13.         {{ ea.crud.customPageTitle is null ? default_title|raw : ea.crud.customPageTitle('index')|trans(ea.i18n.translationParameters)|raw }}
  14.     {%- endapply -%}
  15. {% endblock %}
  16. {% block page_actions %}
  17.     {% block global_actions %}
  18.         {% for action in global_actions %}
  19.             {{ include(action.templatePath, { action: action }, with_context = false) }}
  20.         {% endfor %}
  21.     {% endblock global_actions %}
  22. {% endblock page_actions %}
  23. {% block content_header %}
  24.     {{ parent() }}
  25.     {# TODO:
  26.     {% block batch_actions %}
  27.         {% for batchAction in ea.crud.actions.batchActions %}
  28.             {{ include(batchAction.templatePath, { action: batchAction }, with_context = false) }}
  29.         {% endfor %}
  30.         {{ include('@EasyAdmin/crud/includes/_batch_action_modal.html.twig', {}, with_context = false) }}
  31.     {% endblock %}
  32.     #}
  33. {% endblock content_header %}
  34. {% block main %}
  35.     {# sort can be multiple; let's consider the sorting field the first one #}
  36.     {% set sort_field_name = app.request.get('sort')|keys|first %}
  37.     {% set sort_order = app.request.get('sort')|first %}
  38.     {% set some_results_are_hidden = false %}
  39.     {% set has_footer = entities|length != 0 %}
  40.     {% set has_search = ea.crud.isSearchEnabled %}
  41.     {% set has_filters = filters|length > 0 %}
  42.     {% set has_datagrid_tools = has_search or has_filters %}
  43.     {% if has_datagrid_tools %}
  44.         <div class="datagrid-header-tools">
  45.             <div class="datagrid-search">
  46.                 {% block search %}
  47.                     {% if has_search %}
  48.                         <div class="form-action form-action-search">
  49.                             <form method="get">
  50.                                 {% block search_form %}
  51.                                     {# reset the referrer and page number whenever a new query is performed #}
  52.                                     {% set query_parameters = ea.request.query.all|merge({
  53.                                         referrer: null, page: 1,
  54.                                     }) %}
  55.                                     {# browsers remove the query string when submitting forms using GET;
  56.                                        that's why all query string parameters are added as hidden form fields #}
  57.                                     {% for paramName, paramValue in query_parameters|ea_flatten_array %}
  58.                                         <input type="hidden" name="{{ paramName }}" value="{{ paramValue }}">
  59.                                     {% endfor %}
  60.                                     <div class="form-group">
  61.                                         <div class="form-widget">
  62.                                             <input class="form-control" type="search" name="query" value="{{ app.request.get('query') ?? '' }}" placeholder="{{ 'action.search'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}">
  63.                                         </div>
  64.                                     </div>
  65.                                 {% endblock %}
  66.                             </form>
  67.                         </div>
  68.                     {% endif %}
  69.                 {% endblock search %}
  70.             </div>
  71.             <div class="datagrid-filters">
  72.                 {% block filters %}
  73.                     {% if filters|length > 0 %}
  74.                         {% set applied_filters = ea.request.query.all['filters']|default([])|keys %}
  75.                         <div class="btn-group action-filters">
  76.                             <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">
  77.                                 <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 %}
  78.                             </a>
  79.                             {% if applied_filters %}
  80.                                 <a href="{{ ea_url().unset('filters') }}" class="btn btn-secondary action-filters-reset">
  81.                                     <i class="fa fa-close"></i>
  82.                                 </a>
  83.                             {% endif %}
  84.                         </div>
  85.                     {% endif %}
  86.                 {% endblock filters %}
  87.             </div>
  88.         </div>
  89.     {% endif %}
  90.     <div class="content-panel">
  91.         <div class="content-panel-body with-rounded-top with-min-h-250 without-padding {{ not has_footer ? 'without-footer' }}">
  92.             <table class="table datagrid with-rounded-top {{ not has_footer ? 'with-rounded-bottom' }}">
  93.                 <thead>
  94.                 {% block table_head %}
  95.                     <tr>
  96.                         {% if has_batch_actions %}
  97.                             <th width="1px"><span><input type="checkbox" class="form-batch-checkbox-all"></span></th>
  98.                         {% endif %}
  99.                         {% set ea_sort_asc = constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Option\\SortOrder::ASC') %}
  100.                         {% set ea_sort_desc = constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Option\\SortOrder::DESC') %}
  101.                         {% for field in entities|first.fields ?? [] %}
  102.                             {% set is_sorting_field = ea.search.isSortingField(field.property) %}
  103.                             {% set next_sort_direction = is_sorting_field ? (ea.search.sortDirection(field.property) == ea_sort_desc ? ea_sort_asc : ea_sort_desc) : ea_sort_desc %}
  104.                             {% set column_icon = is_sorting_field ? (next_sort_direction == ea_sort_desc ? 'fa-arrow-up' : 'fa-arrow-down') : 'fa-sort' %}
  105.                             <th class="{{ is_sorting_field ? 'sorted' }} {{ field.isVirtual ? 'field-virtual' }} text-{{ field.textAlign }}" dir="{{ ea.i18n.textDirection }}">
  106.                                 {% if field.isSortable %}
  107.                                     <a href="{{ ea_url({ page: 1, sort: { (field.property): next_sort_direction } }).includeReferrer() }}">
  108.                                         {{ field.label|raw }} <i class="fa fa-fw {{ column_icon }}"></i>
  109.                                     </a>
  110.                                 {% else %}
  111.                                     <span>{{ field.label|raw }}</span>
  112.                                 {% endif %}
  113.                             </th>
  114.                         {% endfor %}
  115.                         <th {% if ea.crud.showEntityActionsAsDropdown %}width="10px"{% endif %} dir="{{ ea.i18n.textDirection }}">
  116.                             <span class="sr-only">{{ 'action.entity_actions'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}</span>
  117.                         </th>
  118.                     </tr>
  119.                 {% endblock table_head %}
  120.                 </thead>
  121.                 <tbody>
  122.                 {% block table_body %}
  123.                     {% for entity in entities %}
  124.                         {% if not entity.isAccessible %}
  125.                             {% set some_results_are_hidden = true %}
  126.                         {% else %}
  127.                             <tr data-id="{{ entity.primaryKeyValueAsString }}">
  128.                                 {% if has_batch_actions %}
  129.                                     <td><input type="checkbox" class="form-batch-checkbox" value="{{ entity.primaryKeyValue }}"></td>
  130.                                 {% endif %}
  131.                                 {% for field in entity.fields %}
  132.                                     <td class="{{ field.property == sort_field_name ? 'sorted' }} text-{{ field.textAlign }} {{ field.cssClass }}" dir="{{ ea.i18n.textDirection }}">
  133.                                         {{ include(field.templatePath, { field: field, entity: entity }, with_context = false) }}
  134.                                     </td>
  135.                                 {% endfor %}
  136.                                 {% block entity_actions %}
  137.                                     <td class="actions">
  138.                                         {% if not ea.crud.showEntityActionsAsDropdown %}
  139.                                             {% for action in entity.actions %}
  140.                                                 {{ include(action.templatePath, { action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }}
  141.                                             {% endfor %}
  142.                                         {% else %}
  143.                                             <div class="dropdown dropdown-actions">
  144.                                                 <a class="dropdown-toggle btn btn-secondary btn-sm" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
  145.                                                     <i class="fa fa-fw fa-ellipsis-h"></i>
  146.                                                 </a>
  147.                                                 <div class="dropdown-menu dropdown-menu-right">
  148.                                                     {% for action in entity.actions %}
  149.                                                         {{ include(action.templatePath, { action: action, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }}
  150.                                                     {% endfor %}
  151.                                                 </div>
  152.                                             </div>
  153.                                         {% endif %}
  154.                                     </td>
  155.                                 {% endblock entity_actions %}
  156.                             </tr>
  157.                         {% endif %}
  158.                     {% else %}
  159.                         <tr>
  160.                             <td class="no-results" colspan="100">
  161.                                 {{ 'datagrid.no_results'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}
  162.                             </td>
  163.                         </tr>
  164.                     {% endfor %}
  165.                     {% if some_results_are_hidden %}
  166.                         <tr class="datagrid-row-empty">
  167.                             <td class="text-center" colspan="{{ entities|first.fields|length + 1 }}">
  168.                                 <span class="datagrid-row-empty-message"><i class="fa fa-lock mr-1"></i> {{ 'datagrid.hidden_results'|trans({}, 'EasyAdminBundle') }}</span>
  169.                             </td>
  170.                         </tr>
  171.                     {% endif %}
  172.                 {% endblock table_body %}
  173.                 </tbody>
  174.             </table>
  175.         </div>
  176.         {% if entities|length > 0 %}
  177.             <div class="content-panel-footer without-padding without-border">
  178.                 {% block paginator %}
  179.                     {{ include(ea.templatePath('crud/paginator')) }}
  180.                 {% endblock paginator %}
  181.             </div>
  182.         {% endif %}
  183.     </div>
  184.     {% block delete_form %}
  185.         {{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', with_context = false) }}
  186.     {% endblock delete_form %}
  187.     {% if filters|length > 0 %}
  188.         {{ include('@EasyAdmin/crud/includes/_filters_modal.html.twig') }}
  189.     {% endif %}
  190. {% endblock main %}
  191. {% block body_javascript %}
  192.     {{ parent() }}
  193.     <script type="text/javascript">
  194.         $(function() {
  195.             const customSwitches = document.querySelectorAll('td.field-boolean .custom-control.custom-switch input[type="checkbox"]');
  196.             for (i = 0; i < customSwitches.length; i++) {
  197.                 customSwitches[i].addEventListener('change', function () {
  198.                     const customSwitch = this;
  199.                     const newValue = this.checked;
  200.                     const oldValue = !newValue;
  201.                     const fieldName = this.closest('.custom-switch').dataset.fieldname;
  202.                     const toggleUrl = "{{ ea_url().setAction('edit')|raw }}"
  203.                         + "&entityId=" + this.closest('[data-id]').dataset.id
  204.                         + "&fieldName=" + fieldName
  205.                         + "&newValue=" + newValue.toString();
  206.                     let toggleRequest = $.ajax({ type: "GET", url: toggleUrl, data: {} });
  207.                     toggleRequest.done(function(result) {});
  208.                     toggleRequest.fail(function() {
  209.                         // in case of error, restore the original value and disable the toggle
  210.                         customSwitch.checked = oldValue;
  211.                         customSwitch.disabled = true;
  212.                         customSwitch.closest('.custom-switch').classList.add('disabled');
  213.                     });
  214.                 });
  215.             }
  216.             $('.action-delete').on('click', function(e) {
  217.                 e.preventDefault();
  218.                 const id = $(this).parents('[data-id]').first().data('id');
  219.                 $('#modal-delete').modal({ backdrop: true, keyboard: true })
  220.                     .off('click', '#modal-delete-button')
  221.                     .on('click', '#modal-delete-button', function () {
  222.                         let deleteForm = $('#delete-form');
  223.                         deleteForm.attr('action', deleteForm.attr('action').replace('__entityId_placeholder__', id));
  224.                         deleteForm.trigger('submit');
  225.                     });
  226.             });
  227.             {% if filters|length > 0 %}
  228.             // HTML5 specifies that a <script> tag inserted with innerHTML should not execute
  229.             // https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#Security_considerations
  230.             // That's why we can't use just 'innerHTML'. See https://stackoverflow.com/a/47614491/2804294
  231.             let setInnerHTML = function(element, htmlContent) {
  232.                 element.innerHTML = htmlContent;
  233.                 Array.from(element.querySelectorAll('script')).forEach(oldScript => {
  234.                     const newScript = document.createElement('script');
  235.                     Array.from(oldScript.attributes)
  236.                         .forEach(attr => newScript.setAttribute(attr.name, attr.value));
  237.                     newScript.appendChild(document.createTextNode(oldScript.innerHTML));
  238.                     oldScript.parentNode.replaceChild(newScript, oldScript);
  239.                 });
  240.             };
  241.             document.querySelector('.action-filters-button').addEventListener('click', function(event) {
  242.                 let filterButton = event.currentTarget;
  243.                 let filterModal = document.querySelector(filterButton.dataset.modal);
  244.                 let filterModalBody = filterModal.querySelector('.modal-body');
  245.                 $(filterModal).modal({ backdrop: true, keyboard: true });
  246.                 filterModalBody.innerHTML = '<div class="fa-3x px-3 py-3 text-muted text-center"><i class="fas fa-circle-notch fa-spin"></i></div>';
  247.                 $.get(filterButton.getAttribute('href'), function (response) {
  248.                     setInnerHTML(filterModalBody, response);
  249.                 });
  250.                 event.preventDefault();
  251.                 event.stopPropagation();
  252.             });
  253.             {% endif %}
  254.             {% if has_batch_actions %}
  255.             const titleContent = $('.content-header-title > .title').html();
  256.             $(document).on('click', '.deselect-batch-button', function () {
  257.                 $(this).closest('.content').find(':checkbox.form-batch-checkbox-all').prop('checked', false).trigger('change');
  258.             });
  259.             $(document).on('change', '.form-batch-checkbox-all', function () {
  260.                 $(this).closest('.content').find(':checkbox.form-batch-checkbox').prop('checked', $(this).prop('checked')).trigger('change');
  261.             });
  262.             $(document).on('change', '.form-batch-checkbox', function () {
  263.                 const $content = $(this).closest('.content');
  264.                 let $input = $content.find(':hidden#batch_form_ids');
  265.                 let ids = $input.val() ? $input.val().split(',') : [];
  266.                 const id = $(this).val();
  267.                 if ($(this).prop('checked')) {
  268.                     if (-1 === ids.indexOf(id)) {
  269.                         ids.push(id);
  270.                     }
  271.                 } else {
  272.                     ids = ids.filter(function(value) { return value !== id });
  273.                     $content.find(':checkbox.form-batch-checkbox-all').prop('checked', false);
  274.                 }
  275.                 if (0 === ids.length) {
  276.                     $content.find('.global-actions').show();
  277.                     $content.find('.batch-actions').hide();
  278.                     $content.find('table').removeClass('table-batch');
  279.                 } else {
  280.                     $content.find('.batch-actions').show();
  281.                     $content.find('.global-actions').hide();
  282.                     $content.find('table').addClass('table-batch');
  283.                 }
  284.                 $input.val(ids.join(','));
  285.                 $content.find('.content-header-title > .title').html(0 === ids.length ? titleContent : '');
  286.             });
  287.             $('button[name="batch_form[name]"].ask-confirm-batch-button').on('click', function (event) {
  288.                 event.preventDefault();
  289.                 event.stopPropagation();
  290.                 let $button = $(this);
  291.                 $('#modal-batch-action').modal({ backdrop : true, keyboard : true })
  292.                     .off('click', '#modal-batch-action-button')
  293.                     .on('click', '#modal-batch-action-button', function () {
  294.                         $button.unbind('click');
  295.                         $button.trigger('click');
  296.                     });
  297.             });
  298.             {% endif %}
  299.         });
  300.     </script>
  301.     {% if app.request.get('query') is not empty %}
  302.         <script type="text/javascript">
  303.             const search_query = "{{ ea.search.query|default('')|e('js') }}";
  304.             // the original query is prepended to allow matching exact phrases in addition to single words
  305.             $('#main').find('table tbody td:not(.actions)').highlight($.merge([search_query], search_query.split(' ')));
  306.         </script>
  307.     {% endif %}
  308. {% endblock %}