1 {% extends 'elements/list.html.twig' %}
3 {% import "macros.twig" as macros %}
5 {% set basic_table = true %}
7 {% set nb = nb_members %}
14 'name': 'batch-memberslist'
18 {% macro draw_actions(rclass, member, login) %}
19 <td class="{{ rclass }} center actions_row">
20 {% set actions = callstatic('\\Galette\\Core\\Galette', 'getListActions', member) %}
21 {% for action in actions %}
22 {{ macros.drawListAction(action.label, action.route, action.icon, action.extra_class ?? null) }}
29 'label': _Tn("%count member", "%count members", nb_members)|replace({'%count': nb_members}),
31 'name': 'filter-memberslist'
37 {% block infoline_actions %}
38 {% if login.isAdmin() or login.isStaff() or (login.isGroupManager() and preferences.pref_bool_groupsmanagers_create_member) %}
40 class="ui tiny labeled icon button"
41 href="{{ url_for("addMember") }}"
43 <i class="plus circle green icon" aria-hidden="true"></i>
44 {{ _T("Add a member") }}
50 {% set columns = [] %}
51 {% for column in galette_list %}
52 {% if column.field_id == 'id_adh' %}
53 {% if preferences.pref_show_id %}
54 {% set columns = columns|merge([
57 order: constant("Galette\\Repository\\Members::ORDERBY_ID")
61 {% set columns = columns|merge([
66 {% set columns = columns|merge([
69 order: column.field_id
79 <form action="{{ url_for('filter-memberslist') }}" method="post" id="members_list_search_filter" class="ui form filters">
80 <div class="ui secondary yellow segment">
81 {% if adv_filters is not defined or not adv_filters %}
82 <div class="five fields">
84 <label for="filter_str">{{ _T('Search:') }}</label>
85 <input type="text" name="filter_str" id="filter_str" value="{{ filters.filter_str }}" type="search" placeholder="{{ _T('Enter a value') }}"/>
88 <label for="field_filter">{{ _T('in:') }}</label>
89 <select name="field_filter" id="field_filter" class="ui search dropdown nochosen">
90 {% for key, value in field_filter_options %}
91 <option value="{{ key }}"{% if key == filters.field_filter %} selected="selected"{% endif %}>{{ value }}</option>
96 <label for="filter_str">{{ _T('among:') }}</label>
97 <select name="membership_filter" onchange="form.submit()" class="ui search dropdown nochosen">
98 {% for key, value in membership_filter_options %}
99 <option value="{{ key }}"{% if key == filters.membership_filter %} selected="selected"{% endif %}>{{ value }}</option>
103 <div class="flexend field">
104 <label for="filter_account" class="displaynone">{{ _T('among:') }}</label>
105 <select name="filter_account" onchange="form.submit()" class="ui search dropdown nochosen">
106 {% for key, value in filter_accounts_options %}
107 <option value="{{ key }}"{% if key == filters.filter_account %} selected="selected"{% endif %}>{{ value }}</option>
111 <div class="flexend field">
112 <label for="group_filter" class="displaynone">{{ _T('among:') }}</label>
113 <select name="group_filter" onchange="form.submit()" class="ui search dropdown nochosen">
114 <option value="0">{{ _T('Select a group') }}</option>
115 {% for group in filter_groups_options %}
116 <option value="{{ group.getId() }}"{% if filters.group_filter == group.getId() %} selected="selected"{% endif %}>{{ group.getIndentName()|raw }}</option>
121 <div class="two fields">
123 <div class="inline fields">
124 <label for="email_filter">{{ _T('Members that have an email address:') }}</label>
125 <div class="field inline">
126 <div class="ui radio checkbox">
127 <input type="radio" name="email_filter" id="filter_dc_email" value="{{ constant('Galette\\Repository\\Members::FILTER_DC_EMAIL') }}"{% if filters.email_filter == constant('Galette\\Repository\\Members::FILTER_DC_EMAIL') %} checked="checked"{% endif %}>
128 <label for="filter_dc_email">{{ _T("Don't care") }}</label>
131 <div class="field inline">
132 <div class="ui radio checkbox">
133 <input type="radio" name="email_filter" id="filter_with_email" value="{{ constant('Galette\\Repository\\Members::FILTER_W_EMAIL') }}"{% if filters.email_filter == constant('Galette\\Repository\\Members::FILTER_W_EMAIL') %} checked="checked"{% endif %}>
134 <label for="filter_with_email">{{ _T('With') }}</label>
137 <div class="field inline">
138 <div class="ui radio checkbox">
139 <input type="radio" name="email_filter" id="filter_without_email" value="{{ constant('Galette\\Repository\\Members::FILTER_WO_EMAIL') }}"{% if filters.email_filter == constant('Galette\\Repository\\Members::FILTER_WO_EMAIL') %} checked="checked"{% endif %}>
140 <label for="filter_without_email">{{ _T('Without') }}</label>
145 <div class="ui right aligned basic fitted segment field">
146 <button type="submit" class="tooltip action ui labeled icon primary button" title="{{ _T('Apply filters') }}" name="filter">
147 <i class="search icon"></i>
150 <button type="submit" class="tooltip action ui labeled icon button" title="{{ _T('Save selected criteria') }}" name="savesearch" id="savesearch">
151 <i class="save blue icon"></i>
154 <button type="submit" name="clear_filter" class="tooltip ui labeled icon button" title="{{ _T('Reset all filters to defaults') }}">
155 <i class="trash alt red icon" aria-hidden="true"></i>
156 {{ _T('Clear filter') }}
162 <span class="ui primary ribbon label">{{ _T('Advanced search mode') }}</span>
163 <button type="submit" class="tooltip action ui labeled icon primary button" title="{{ _T('Save current advanced search criteria') }}" name="savesearch" id="savesearch">
164 <i class="save icon"></i>
167 <button type="submit" class="tooltip action ui labeled icon button" title="{{ _T('Change search criteria') }}" name="adv_criteria">
168 <i class="edit blue icon"></i>
169 {{ _T('Change criteria') }}
171 <input type="hidden" name="advanced_search" value="1" class="ui button"/>
172 <button type="submit" name="clear_filter" class="tooltip ui labeled icon button" title="{{ _T('Reset all filters to defaults') }}">
173 <i class="trash alt red icon" aria-hidden="true"></i>
174 {{ _T('Clear filter') }}
176 <div class="ui basic fluid accordion">
178 <i class="dropdown icon"></i>
179 {{ _T('Show/hide query') }}
181 <div class="content">
182 <div id="sql_qry" class="ui grey inverted segment">{{ filters.query }}</div>
187 {% include "components/forms/csrf.html.twig" %}
193 {% if nb_members != 0 %}
194 <div id="legende" title="{{ _T('Legend') }}" class="ui modal">
195 <div class="header">{{ _T('Legend') }}</div>
196 <div class="content">
197 <table class="ui stripped table">
200 <th class="" colspan="4">{{ _T('Reading the list') }}</th>
205 <th class="back">{{ _T('Name') }}</th>
206 <td class="back">{{ _T('Active account') }}</td>
207 <th class="inactive-account back">{{ _T('Name') }}</th>
208 <td class="back">{{ _T('Inactive account') }}</td>
211 <th class="cotis-ok color-sample"> </th>
212 <td class="back">{{ _T('Membership in order') }}</td>
213 <th class="cotis-soon color-sample"> </th>
214 <td class="back">{{ _T('Membership will expire soon (<30d)') }}</td>
217 <th class="cotis-never color-sample"> </th>
218 <td class="back">{{ _T('Never contributed') }}</td>
219 <th class="cotis-late color-sample"> </th>
220 <td class="back">{{ _T('Lateness in fee') }}</td>
224 <table class="ui stripped table">
227 <th class="" colspan="4">{{ _T('Actions') }}</th>
233 <i class="ui user edit blue icon"></i>
235 <td class="back">{{ _T('Modification') }}</td>
237 <i class="ui receipt yellow icon"></i>
239 <td class="back">{{ _T('Contributions') }}</td>
243 <i class="ui user times red icon"></i>
245 <td class="back">{{ _T('Deletion') }}</td>
249 <table class="ui stripped table">
252 <th colspan="4">{{ _T('User status/interactions') }}</th>
257 <th><i class="ui envelope outline teal icon"></i></th>
258 <td class="back">{{ _T('Send an email') }}</td>
259 <th><i class="ui building icon"></i></th>
260 <td class="back">{{ _T('Is a company') }}</td>
264 <th><i class="ui male icon"></i></th>
265 <td class="back">{{ _T('Is a man') }}</td>
266 <th><i class="ui female icon"></i></th>
267 <td class="back">{{ _T('Is a woman') }}</td>
270 <th><i class="ui user shield red icon"></i></th>
271 <td class="back">{{ _T('Admin') }}</td>
272 <th><i class="ui id card alternate orange icon"></i></th>
273 <td class="back">{{ _T('Staff member') }}</td>
279 <div class="actions"><div class="ui labeled icon deny button"><i class="times icon"></i> {{ _T('Close') }}</div></div>
285 {% for ordre, member in members %}
286 {% set rclass = member.getRowClass() %}
288 {% for column in galette_list %}
289 {% if column.field_id == 'id_adh' %}
290 <td class="{{ rclass }} right" data-scope="id">
291 {% if preferences.pref_show_id %}
294 {{ ordre + 1 + (filters.current_page - 1) * numrows }}
297 {% elseif column.field_id == 'list_adh_name' %}
298 <td class="{{ rclass }} username_row" data-scope="row">
299 <input type="checkbox" name="entries_sel[]" value="{{ member.__get('id') }}"/>
300 {% if member.isCompany() %}
302 <i class="ui building outline icon tooltip"></i>
303 <span class="ui special popup">{{ _T('Is a company') }}</span>
305 {% elseif member.isMan() %}
307 <i class="ui male icon tooltip"></i>
308 <span class="ui special popup">{{ _T('Is a man') }}</span>
310 {% elseif member.isWoman() %}
312 <i class="ui female icon tooltip"></i>
313 <span class="ui special popup">{{ _T('Is a woman') }}</span>
316 <i class="ui icon"></i>
318 {% if member.email != '' %}
319 <a href="mailto:{{ member.email }}">
320 <i class="ui envelope outline teal icon tooltip" aria-hidden="true"></i>
321 <span class="ui special popup">{{ _T('Mail') }}</span>
325 <i class="ui icon"></i>
327 {% if member.isAdmin() %}
329 <i class="ui user shield red icon tooltip" aria-hidden="true"></i>
330 <span class="ui special popup">{{ _T('Admin') }}</span>
332 {% elseif member.isStaff() %}
334 <i class="ui id card alternate orange icon tooltip" aria-hidden="true"></i>
335 <span class="ui special popup">{{ _T('Staff member') }}</span>
337 {# TODO: display for group managers #}
339 <i class="ui icon"></i>
341 {% set mid = member.id %}
342 <a href="{{ url_for("member", {"id": member.id}) }}">{{ member.sname }}{% if member.company_name %} ({{ member.company_name }}){% endif %}</a>
345 {% set lrclass = rclass %}
346 {% set propname = column.propname %}
347 {% set propvalue = attribute(member, propname) %}
348 {% set value = null %}
350 {% if column.field_id == 'nom_adh' %}
351 {% set value = member.sfullname %}
352 {% elseif column.field_id == 'pseudo_adh' %}
353 {% set lrclass = rclass %}
354 {% set value = attribute(member, propname) %}
355 {% elseif column.field_id == 'tel_adh' or column.field_id == 'gsm_adh' %}
356 {% set lrclass = rclass %}
357 {% elseif column.field_id == 'id_statut' %}
358 {% set lrclass = rclass %}
359 {% set value = member.sstatus %}
360 {% elseif column.field_id == 'titre_adh' %}
361 {% if member.title is iterable %}
362 {% set value = member.title.long %}
364 {% elseif column.field_id == 'pref_lang' %}
365 {% set value = i18n.getNameFromId(member.language) %}
366 {% elseif column.field_id == 'adresse_adh' %}
367 {% set value = member.saddress|e('html')|nl2br %}
368 {% set escaped = true %}
369 {% elseif column.field_id == 'bool_display_info' %}
370 {% set value = member.sappears_in_list %}
371 {% elseif column.field_id == 'activite_adh' %}
372 {% set value = member.sactive %}
373 {% elseif column.field_id == 'id_statut' %}
374 {% set value = member.sstatus %}
375 {% elseif column.field_id == 'bool_admin_adh' %}
376 {% set value = member.sadmin %}
377 {% elseif column.field_id == 'bool_exempt_adh' %}
378 {% set value = member.sdue_free %}
379 {% elseif column.field_id == 'sexe_adh' %}
380 {% set value = member.sgender %}
383 {# If value has not been set, take the generic value #}
386 {% set value = propvalue|e('html') %}
388 {% set value = propvalue %}
390 {% elseif escaped is not defined %}
391 {% set value = value|e('html') %}
394 <td class="{{ lrclass }}" data-title="{{ column.label }}">
396 A check is done here to adapt display, this is may not the best way to go
397 but for now, that works as expected.
400 {% if column.field_id == 'email_adh' %}
401 <a href="mailto:{{ value }}">{{ value }}</a>
402 {% elseif column.field_id == 'tel_adh' or column.field_id == 'gsm_adh' %}
403 <a href="tel:{{ value }}">{{ value }}</a>
404 {% elseif column.field_id == 'parent_id' %}
405 <a href="{{ url_for("member", {"id": member.parent}) }}">{{ memberName({"id": member.parent}) }}</a>
406 {% elseif column.field_id == 'ddn_adh' %}
407 {{ value }} {{ member.getAge() }}
415 {{ _self.draw_actions(rclass, member, login) }}
418 <tr><td colspan="{{ galette_list|length + 1 }}" class="emptylist">{{ _T('No member has been found') }}</td></tr>
422 {% if nb_members != 0 and (login.isGroupManager() and (preferences.pref_bool_groupsmanagers_exports or preferences.pref_bool_groupsmanagers_mailings) or login.isAdmin() or login.isStaff()) %}
425 'name': 'batch-memberslist'
428 'title': _T("No member selected"),
429 'content': _T("Please make sure to select at least one member from the list to perform this action.")
432 {% set batch_actions = callstatic('\\Galette\\Core\\Galette', 'getBatchActions') %}
435 {% block batch_selection %}
436 else if (value == 'sendmail') {
437 {% if existing_mailing == true %}
443 else if (value == 'attendance_sheet') {
444 _attendance_sheet_details();
447 else if (value == 'masscontributions') {
448 _masscontributions();
451 else if (value == 'masschange') {
456 {% block javascripts %}
457 {% set batch_selection = ['masschange', 'masscontributions', 'sendmail', 'attendance_sheet_details'] %}
459 <script type="text/javascript">
460 {# Use of Javascript to draw specific elements that are not relevant is JS is inactive #}
462 $('#savesearch').on('click', function(e) {
466 title: '{{ _T("Search title")|e('js') }}',
468 content: '<div class="ui input"><input type="text" name="search_title" id="search_title"/></div>',
469 onApprove: function() {
470 var _form = $('#members_list_search_filter');
471 var _data = _form.serialize();
472 _data = _data + "&search_title=" + $('#search_title').val();
474 url: '{{ url_for('saveSearch') }}',
478 {% include "elements/js/loader.js.twig" with {
480 selector: ".loader_selector"
482 success: function(res) {
484 url: '{{ url_for('ajaxMessages') }}',
486 success: function (message) {
487 $('#asso_name').after(message);
494 text : '{{ _T("Save")|e('js') }}',
496 class : 'icon labeled primary approve'
498 text : '{{ _T("Cancel")|e('js') }}',
500 class : 'icon labeled cancel'
506 {% if nb_members != 0 %}
507 var _sendmail = function(existing){
508 var _form = $('#listform');
509 _form.append($('<input type="hidden" name="sendmail" value="true"/>'));
510 _form.append($('<input type="hidden" name="mailing_new" value="true"/>'));
511 _form.append($('<input type="hidden" name="mailing" value="true"/>'));
512 var _redirect = '{{ url_for('mailing') }}';
515 {% include "elements/js/modal.js.twig" with {
516 modal_title_twig: _T("Existing mailing")|e("js"),
517 modal_content_twig: _T("A mailing already exists. Do you want to create a new one or resume the existing?")|e("js"),
519 modal_onapprove: "_form.submit();",
520 modal_approve_text: _T("New")|e("js"),
521 modal_approve_icon: "plus",
522 modal_approve_color: "green",
523 modal_additional_button: {
524 text: _T("Resume")|e("js"),
527 click: "function() { location.href = _redirect; }"
534 var _attendance_sheet_details = function(){
536 url: '{{ url_for('attendance_sheet_details') }}',
540 selection: $('#listform input[type=\"checkbox\"]:checked').map(function(){
541 return $(this).val();
545 {% include "elements/js/loader.js.twig" with {
547 selector: ".loader_selector"
549 success: function(res){
552 $('body').append(_res);
554 {% include "elements/js/modal.js.twig" with {
555 modal_selector: "#attendance_sheet_details",
556 modal_without_title: true,
558 modal_content_class: "scrolling",
559 modal_onshow: "_batchAjaxMapper();",
560 modal_onapprove: "$('#attendance_sheet_details form').submit();",
561 modal_ondeny: "$(this).modal('hide dimmer').remove();"
565 {% include "elements/js/modal.js.twig" with {
566 modal_title_twig: _T("An error occurred displaying attendance sheet details interface :(")|e("js"),
567 modal_without_content: true,
569 modal_deny_only: true,
570 modal_cancel_text: _T("Close")|e("js"),
571 modal_classname: "redalert",
576 var _masscontributions = function(){
578 url: '{{ url_for('batch-memberslist') }}',
582 masscontributions: true,
583 entries_sel: $('#listform input[type=\"checkbox\"]:checked').map(function(){
584 return $(this).val();
588 {% include "elements/js/loader.js.twig" with {
590 selector: ".loader_selector"
592 success: function(res){
595 $('body').append(_res);
597 _res.find('form').on('submit', function(e) {
600 var _data = _form.serialize();
603 url: _form.attr('action'),
607 {% include "elements/js/loader.js.twig" with {
609 selector: ".loader_selector"
611 success: function(data, status, xhr) {
614 $('#mass_contributions').remove();
615 $('body').append(_res);
617 _res.find('form').on('submit', function(e) {
620 var _data = _form.serialize();
622 url: _form.attr('action'),
626 {% include "elements/js/loader.js.twig" with {
628 selector: ".loader_selector"
630 success: function(res) {
631 window.location.href = _form.find('input[name=redirect_uri]').val();
634 {% include "elements/js/modal.js.twig" with {
635 modal_title_twig: _T("An error occurred :(")|e("js"),
636 modal_without_content: true,
638 modal_deny_only: true,
639 modal_cancel_text: _T("Close")|e("js"),
640 modal_classname: "redalert",
646 {% include "elements/js/modal.js.twig" with {
647 modal_selector: "#mass_contributions",
648 modal_without_title: true,
649 modal_content_class: "scrolling",
650 modal_onshow: "_batchAjaxMapper();",
651 modal_onapprove: "$('#mass_contributions form').submit();",
652 modal_ondeny: "$(this).modal('hide dimmer').remove();"
656 {% include "elements/js/modal.js.twig" with {
657 modal_title_twig: _T("An error occurred :(")|e("js"),
658 modal_without_content: true,
660 modal_deny_only: true,
661 modal_cancel_text: _T("Close")|e("js"),
662 modal_classname: "redalert",
668 {% include "elements/js/modal.js.twig" with {
669 modal_selector: "#mass_contributions",
670 modal_without_title: true,
672 modal_onshow: "_batchAjaxMapper();",
673 modal_onapprove: "$('#mass_contributions form').submit();",
674 modal_ondeny: "$(this).modal('hide dimmer').remove();"
678 {% include "elements/js/modal.js.twig" with {
679 modal_title_twig: _T("An error occurred :(")|e("js"),
680 modal_without_content: true,
682 modal_deny_only: true,
683 modal_cancel_text: _T("Close")|e("js"),
684 modal_classname: "redalert",
689 var _masschange = function(){
691 url: '{{ url_for('batch-memberslist') }}',
696 entries_sel: $('#listform input[type=\"checkbox\"]:checked').map(function(){
697 return $(this).val();
701 {% include "elements/js/loader.js.twig" with {
703 selector: ".loader_selector"
705 success: function(res){
708 $('body').append(_res);
710 _res.find('form').on('submit', function(e) {
713 var _data = _form.serialize();
715 url: _form.attr('action'),
719 {% include "elements/js/loader.js.twig" with {
721 selector: ".loader_selector"
723 success: function(data, status, xhr) {
726 $('#mass_change').remove();
727 $('body').append(_res);
729 _res.find('form').on('submit', function(e) {
732 var _data = _form.serialize();
734 url: _form.attr('action'),
738 {% include "elements/js/loader.js.twig" with {
740 selector: ".loader_selector"
742 success: function(res) {
743 window.location.href = _form.find('input[name=redirect_uri]').val();
746 {% include "elements/js/modal.js.twig" with {
747 modal_title_twig: _T("An error occurred :(")|e("js"),
748 modal_without_content: true,
750 modal_deny_only: true,
751 modal_cancel_text: _T("Close")|e("js"),
752 modal_classname: "redalert",
758 {% include "elements/js/modal.js.twig" with {
759 modal_selector: "#mass_change",
760 modal_without_title: true,
761 modal_onapprove: "$('#mass_change form').submit();",
762 modal_ondeny: "$(this).modal('hide dimmer').remove();"
766 {% include "elements/js/modal.js.twig" with {
767 modal_title_twig: _T("An error occurred :(")|e("js"),
768 modal_without_content: true,
770 modal_deny_only: true,
771 modal_cancel_text: _T("Close")|e("js"),
772 modal_classname: "redalert",
778 {% include "elements/js/modal.js.twig" with {
779 modal_selector: "#mass_change",
780 modal_without_title: true,
781 modal_content_class: "scrolling",
782 modal_onshow: "_massCheckboxes('#mass_change');_batchAjaxMapper();",
783 modal_onapprove: "$('#mass_change form').submit();",
784 modal_ondeny: "$(this).modal('hide dimmer').remove();"
788 {% include "elements/js/modal.js.twig" with {
789 modal_title_twig: _T("An error occurred :(")|e("js"),
790 modal_without_content: true,
792 modal_deny_only: true,
793 modal_cancel_text: _T("Close")|e("js"),
794 modal_classname: "redalert",
799 var _batchAjaxMapper = function(){
800 $('.modal-form .ui.dropdown, .modal-form select:not(.nochosen)').dropdown();
801 $('.modal-form .ui.checkbox, .modal-form .ui.radio.checkbox').checkbox();
802 $('.no-touch .modal-form a[title], .no-touch .modal-form .tooltip').popup({
803 variation: 'inverted',
806 {% include 'elements/js/calendar.js.twig' with {
807 selector: ".modal-form [id$='rangestart'], .modal-form [id$='rangeend']"