]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Controllers/Crud/MembersController.php
f98093c42c03461b1e018f96d7cf148f310f0eed
[galette.git] / galette / lib / Galette / Controllers / Crud / MembersController.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Galette members controller
7 *
8 * PHP version 5
9 *
10 * Copyright © 2019-2023 The Galette Team
11 *
12 * This file is part of Galette (http://galette.tuxfamily.org).
13 *
14 * Galette is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * Galette is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Galette. If not, see <http://www.gnu.org/licenses/>.
26 *
27 * @category Controllers
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2019-2023 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.9.4dev - 2019-12-02
35 */
36
37 namespace Galette\Controllers\Crud;
38
39 use Galette\Controllers\CrudController;
40 use Galette\DynamicFields\Boolean;
41 use Galette\Features\BatchList;
42 use Slim\Psr7\Request;
43 use Slim\Psr7\Response;
44 use Galette\Core\GaletteMail;
45 use Galette\Core\Gaptcha;
46 use Galette\Entity\Adherent;
47 use Galette\Entity\Contribution;
48 use Galette\Entity\ContributionsTypes;
49 use Galette\Entity\DynamicFieldsHandle;
50 use Galette\Entity\Group;
51 use Galette\Entity\Status;
52 use Galette\Entity\FieldsConfig;
53 use Galette\Entity\Social;
54 use Galette\Filters\AdvancedMembersList;
55 use Galette\Filters\MembersList;
56 use Galette\Repository\Groups;
57 use Galette\Repository\Members;
58 use Galette\Repository\PaymentTypes;
59 use Galette\Repository\Titles;
60 use Analog\Analog;
61
62 /**
63 * Galette members controller
64 *
65 * @category Controllers
66 * @name GaletteController
67 * @package Galette
68 * @author Johan Cwiklinski <johan@x-tnd.be>
69 * @copyright 2019-2023 The Galette Team
70 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
71 * @link http://galette.tuxfamily.org
72 * @since Available since 0.9.4dev - 2019-12-02
73 */
74
75 class MembersController extends CrudController
76 {
77 use BatchList;
78
79 /** @var bool */
80 private $is_self_membership = false;
81
82 // CRUD - Create
83
84 /**
85 * Add page
86 *
87 * @param Request $request PSR Request
88 * @param Response $response PSR Response
89 *
90 * @return Response
91 */
92 public function add(Request $request, Response $response): Response
93 {
94 return $this->edit($request, $response, null, 'add');
95 }
96
97 /**
98 * Add child page
99 *
100 * @param Request $request PSR Request
101 * @param Response $response PSR Response
102 *
103 * @return Response
104 */
105 public function addChild(Request $request, Response $response): Response
106 {
107 return $this->edit($request, $response, null, 'addchild');
108 }
109
110 /**
111 * Self subscription page
112 *
113 * @param Request $request PSR Request
114 * @param Response $response PSR Response
115 *
116 * @return Response
117 */
118 public function selfSubscribe(Request $request, Response $response): Response
119 {
120 if (!$this->preferences->pref_bool_selfsubscribe || $this->login->isLogged()) {
121 return $response
122 ->withStatus(301)
123 ->withHeader('Location', $this->routeparser->urlFor('slash'));
124 }
125
126 if ($this->session->member !== null) {
127 $member = $this->session->member;
128 $this->session->member = null;
129 } else {
130 $member = new Adherent($this->zdb);
131 $member->enableDep('dynamics');
132 }
133
134 //mark as self membership
135 $member->setSelfMembership();
136
137 // flagging required fields
138 $fc = $this->fields_config;
139 $form_elements = $fc->getFormElements($this->login, true, true);
140
141 // members
142 $m = new Members();
143 $members = $m->getDropdownMembers(
144 $this->zdb,
145 $this->login,
146 $member->hasParent() ? $member->parent->id : null
147 );
148
149 $params = [
150 'members' => [
151 'filters' => $m->getFilters(),
152 'count' => $m->getCount()
153 ]
154 ];
155
156 if (count($members)) {
157 $params['members']['list'] = $members;
158 }
159
160 $gaptcha = new Gaptcha($this->i18n);
161 $this->session->gaptcha = $gaptcha;
162
163 $titles = new Titles($this->zdb);
164
165 // display page
166 $this->view->render(
167 $response,
168 'pages/member_form.html.twig',
169 array(
170 'page_title' => _T("Subscription"),
171 'parent_tpl' => 'public_page.html.twig',
172 'member' => $member,
173 'self_adh' => true,
174 'autocomplete' => true,
175 'osocials' => new Social($this->zdb),
176 // pseudo random int
177 'time' => time(),
178 'titles_list' => $titles->getList(),
179 'fieldsets' => $form_elements['fieldsets'],
180 'hidden_elements' => $form_elements['hiddens'],
181 //self_adh specific
182 'gaptcha' => $gaptcha
183 ) + $params
184 );
185 return $response;
186 }
187
188 /**
189 * Add action
190 *
191 * @param Request $request PSR Request
192 * @param Response $response PSR Response
193 *
194 * @return Response
195 */
196 public function doAdd(Request $request, Response $response): Response
197 {
198 return $this->store($request, $response);
199 }
200
201 /**
202 * Self subscription add action
203 *
204 * @param Request $request PSR Request
205 * @param Response $response PSR Response
206 *
207 * @return Response
208 */
209 public function doSelfSubscribe(Request $request, Response $response): Response
210 {
211 $this->setSelfMembership();
212 return $this->doAdd($request, $response);
213 }
214
215
216 /**
217 * Duplicate action
218 *
219 * @param Request $request PSR Request
220 * @param Response $response PSR Response
221 * @param integer $id_adh Member ID to duplicate
222 *
223 * @return Response
224 */
225 public function duplicate(Request $request, Response $response, int $id_adh): Response
226 {
227 $adh = new Adherent($this->zdb, $id_adh, ['dynamics' => true, 'parent' => true]);
228 $adh->setDuplicate();
229
230 //store entity in session
231 $this->session->member = $adh;
232
233 return $response
234 ->withStatus(301)
235 ->withHeader('Location', $this->routeparser->urlFor('addMember'));
236 }
237
238 // /CRUD - Create
239 // CRUD - Read
240
241 /**
242 * Display member card
243 *
244 * @param Request $request PSR Request
245 * @param Response $response PSR Response
246 * @param integer $id Member ID
247 *
248 * @return Response
249 */
250 public function show(Request $request, Response $response, int $id): Response
251 {
252 $member = new Adherent($this->zdb);
253 $member
254 ->enableAllDeps()
255 ->load($id);
256
257 if (!$member->canShow($this->login)) {
258 $this->flash->addMessage(
259 'error_detected',
260 _T("You do not have permission for requested URL.")
261 );
262
263 return $response
264 ->withStatus(301)
265 ->withHeader(
266 'Location',
267 $this->routeparser->urlFor('me')
268 );
269 }
270
271 if ($member->id == null) {
272 //member does not exist!
273 $this->flash->addMessage(
274 'error_detected',
275 str_replace('%id', $id, _T("No member #%id."))
276 );
277
278 return $response
279 ->withHeader(
280 'Location',
281 $this->routeparser->urlFor('slash')
282 );
283 }
284
285 // flagging fields visibility
286 $fc = $this->fields_config;
287 $display_elements = $fc->getDisplayElements($this->login);
288
289 // display page
290 $this->view->render(
291 $response,
292 'pages/member_show.html.twig',
293 array(
294 'page_title' => _T("Member Profile"),
295 'member' => $member,
296 'pref_lang' => $this->i18n->getNameFromId($member->language),
297 'pref_card_self' => $this->preferences->pref_card_self,
298 'groups' => Groups::getSimpleList(),
299 'time' => time(),
300 'display_elements' => $display_elements,
301 'osocials' => new Social($this->zdb),
302 'navigate' => $this->handleNavigationLinks($member->id)
303 )
304 );
305 return $response;
306 }
307
308 /**
309 * Own card show
310 *
311 * @param Request $request PSR Request
312 * @param Response $response PSR Response
313 *
314 * @return Response
315 */
316 public function showMe(Request $request, Response $response): Response
317 {
318 if ($this->login->isSuperAdmin()) {
319 return $response
320 ->withStatus(301)
321 ->withHeader('Location', $this->routeparser->urlFor('slash'));
322 }
323 return $this->show($request, $response, $this->login->id);
324 }
325
326 /**
327 * Public pages (trombinoscope, public list)
328 *
329 * @param Request $request PSR Request
330 * @param Response $response PSR Response
331 * @param string $option One of 'page' or 'order'
332 * @param string|integer $value Value of the option
333 * @param string $type List type (either list or trombi)
334 *
335 * @return Response
336 */
337 public function publicList(
338 Request $request,
339 Response $response,
340 $option = null,
341 $value = null,
342 $type = null
343 ): Response {
344 $varname = 'public_filter_' . $type;
345 if (isset($this->session->$varname)) {
346 $filters = $this->session->$varname;
347 } else {
348 $filters = new MembersList();
349 }
350
351 if ($option !== null) {
352 switch ($option) {
353 case 'page':
354 $filters->current_page = (int)$value;
355 break;
356 case 'order':
357 $filters->orderby = $value;
358 break;
359 }
360 }
361
362 $m = new Members($filters);
363 $members = $m->getPublicList($type === 'trombi');
364
365 $this->session->$varname = $filters;
366
367 //assign pagination variables to the template and add pagination links
368 $filters->setViewPagination($this->routeparser, $this->view, false);
369
370 // display page
371 $this->view->render(
372 $response,
373 ($type === 'list' ? 'pages/members_public_list' : 'pages/members_public_gallery') . '.html.twig',
374 array(
375 'page_title' => ($type === 'list' ? _T("Members list") : _T('Trombinoscope')),
376 'additionnal_html_class' => ($type === 'list' ? '' : 'trombinoscope'),
377 'type' => $type,
378 'members' => $members,
379 'nb_members' => $m->getCount(),
380 'filters' => $filters,
381 // pseudo random int
382 'time' => time(),
383 )
384 );
385 return $response;
386 }
387
388 /**
389 * Public pages (trombinoscope, public list)
390 *
391 * @param Request $request PSR Request
392 * @param Response $response PSR Response
393 * @param string $type Type
394 *
395 * @return Response
396 */
397 public function filterPublicList(Request $request, Response $response, string $type): Response
398 {
399 $post = $request->getParsedBody();
400
401 $varname = 'public_filter_' . $type;
402 if (isset($this->session->$varname)) {
403 $filters = $this->session->$varname;
404 } else {
405 $filters = new MembersList();
406 }
407
408 //reintialize filters
409 if (isset($post['clear_filter'])) {
410 $filters->reinit();
411 } else {
412 //number of rows to show
413 if (isset($post['nbshow'])) {
414 $filters->show = (int)$post['nbshow'];
415 }
416 }
417
418 $this->session->$varname = $filters;
419
420 return $response
421 ->withStatus(301)
422 ->withHeader('Location', $this->routeparser->urlFor('publicList', ['type' => $type]));
423 }
424
425 /**
426 * Members list
427 *
428 * @param Request $request PSR Request
429 * @param Response $response PSR Response
430 * @param string $option One of 'page' or 'order'
431 * @param string|integer $value Value of the option
432 *
433 * @return Response
434 */
435 public function list(Request $request, Response $response, $option = null, $value = null): Response
436 {
437 if (isset($this->session->filter_members)) {
438 $filters = $this->session->filter_members;
439 } else {
440 $filters = new MembersList();
441 }
442
443 if ($option !== null) {
444 switch ($option) {
445 case 'page':
446 $filters->current_page = (int)$value;
447 break;
448 case 'order':
449 $filters->orderby = $value;
450 break;
451 }
452 }
453
454 $members = new Members($filters);
455
456 $members_list = array();
457 if ($this->login->isAdmin() || $this->login->isStaff()) {
458 $members_list = $members->getMembersList(true);
459 } else {
460 $members_list = $members->getManagedMembersList(true);
461 }
462
463 $groups = new Groups($this->zdb, $this->login);
464 $groups_list = $groups->getList();
465
466 //assign pagination variables to the template and add pagination links
467 $filters->setViewPagination($this->routeparser, $this->view, false);
468 $filters->setViewCommonsFilters($this->preferences, $this->view);
469
470 $this->session->filter_members = $filters;
471
472 // display page
473 $this->view->render(
474 $response,
475 'pages/members_list.html.twig',
476 array(
477 'page_title' => _T("Members management"),
478 'require_mass' => true,
479 'members' => $members_list,
480 'filter_groups_options' => $groups_list,
481 'nb_members' => $members->getCount(),
482 'filters' => $filters,
483 'adv_filters' => $filters instanceof AdvancedMembersList,
484 'galette_list' => $this->lists_config->getDisplayElements($this->login)
485 )
486 );
487 return $response;
488 }
489
490 /**
491 * Members filtering
492 *
493 * @param Request $request PSR Request
494 * @param Response $response PSR Response
495 *
496 * @return Response
497 */
498 public function filter(Request $request, Response $response): Response
499 {
500 $post = $request->getParsedBody();
501 if (isset($this->session->filter_members)) {
502 //CAUTION: this one may be simple or advanced, display must change
503 $filters = $this->session->filter_members;
504 } else {
505 $filters = new MembersList();
506 }
507
508 //reintialize filters
509 if (isset($post['clear_filter'])) {
510 $filters = new MembersList();
511 } elseif (isset($post['clear_adv_filter'])) {
512 $this->session->filter_members = null;
513 unset($this->session->filter_members);
514
515 return $response
516 ->withStatus(301)
517 ->withHeader('Location', $this->routeparser->urlFor('advanced-search'));
518 } elseif (isset($post['adv_criteria'])) {
519 return $response
520 ->withStatus(301)
521 ->withHeader('Location', $this->routeparser->urlFor('advanced-search'));
522 } else {
523 //string to filter
524 if (isset($post['filter_str'])) { //filter search string
525 $filters->filter_str = stripslashes(
526 htmlspecialchars($post['filter_str'], ENT_QUOTES)
527 );
528 }
529 //field to filter
530 if (isset($post['field_filter'])) {
531 if (is_numeric($post['field_filter'])) {
532 $filters->field_filter = $post['field_filter'];
533 }
534 }
535 //membership to filter
536 if (isset($post['membership_filter'])) {
537 if (is_numeric($post['membership_filter'])) {
538 $filters->membership_filter
539 = $post['membership_filter'];
540 }
541 }
542 //account status to filter
543 if (isset($post['filter_account'])) {
544 if (is_numeric($post['filter_account'])) {
545 $filters->filter_account = $post['filter_account'];
546 }
547 }
548 //email filter
549 if (isset($post['email_filter'])) {
550 $filters->email_filter = (int)$post['email_filter'];
551 }
552 //group filter
553 if (
554 isset($post['group_filter'])
555 && $post['group_filter'] > 0
556 ) {
557 $filters->group_filter = (int)$post['group_filter'];
558 }
559 //number of rows to show
560 if (isset($post['nbshow'])) {
561 $filters->show = (int)$post['nbshow'];
562 }
563
564 if (isset($post['advanced_filtering'])) {
565 if (!$filters instanceof AdvancedMembersList) {
566 $filters = new AdvancedMembersList($filters);
567 }
568 //Advanced filters
569 $filters->reinit();
570 unset($post['advanced_filtering']);
571 $freed = false;
572 foreach ($post as $k => $v) {
573 if (strpos($k, 'free_', 0) === 0) {
574 if (!$freed) {
575 $i = 0;
576 foreach ($post['free_field'] as $f) {
577 if (
578 trim($f) !== ''
579 && trim($post['free_text'][$i]) !== ''
580 ) {
581 $fs_search = htmlspecialchars($post['free_text'][$i], ENT_QUOTES);
582 $log_op
583 = (int)$post['free_logical_operator'][$i];
584 $qry_op
585 = (int)$post['free_query_operator'][$i];
586 $type = (int)$post['free_type'][$i];
587 $fs = array(
588 'idx' => $i,
589 'field' => $f,
590 'type' => $type,
591 'search' => $fs_search,
592 'log_op' => $log_op,
593 'qry_op' => $qry_op
594 );
595 $filters->free_search = $fs;
596 }
597 $i++;
598 }
599 $freed = true;
600 }
601 } elseif ($k == 'groups_search') {
602 $i = 0;
603 $filters->groups_search_log_op = (int)$post['groups_logical_operator'];
604 foreach ($post['groups_search'] as $g) {
605 if (trim($g) !== '') {
606 $gs = array(
607 'idx' => $i,
608 'group' => $g
609 );
610 $filters->groups_search = $gs;
611 }
612 $i++;
613 }
614 } else {
615 switch ($k) {
616 case 'contrib_min_amount':
617 case 'contrib_max_amount':
618 if (trim($v) !== '') {
619 $v = (float)$v;
620 } else {
621 $v = null;
622 }
623 break;
624 }
625 $filters->$k = $v;
626 }
627 }
628 }
629 }
630
631 if (isset($post['savesearch'])) {
632 return $response
633 ->withStatus(301)
634 ->withHeader(
635 'Location',
636 $this->routeparser->urlFor(
637 'saveSearch',
638 $post
639 )
640 );
641 }
642
643 $this->session->filter_members = $filters;
644
645 return $response
646 ->withStatus(301)
647 ->withHeader('Location', $this->routeparser->urlFor('members'));
648 }
649
650 /**
651 * Advanced search page
652 *
653 * @param Request $request PSR Request
654 * @param Response $response PSR Response
655 *
656 * @return Response
657 */
658 public function advancedSearch(Request $request, Response $response): Response
659 {
660 if (isset($this->session->filter_members)) {
661 $filters = $this->session->filter_members;
662 if (!$filters instanceof AdvancedMembersList) {
663 $filters = new AdvancedMembersList($filters);
664 }
665 } else {
666 $filters = new AdvancedMembersList();
667 }
668
669 $groups = new Groups($this->zdb, $this->login);
670 $groups_list = $groups->getList();
671
672 //we want only visible fields
673 $fields = $this->members_fields;
674 $fc = $this->fields_config;
675 $fc->filterVisible($this->login, $fields);
676
677 //add status label search
678 if ($pos = array_search(Status::PK, array_keys($fields))) {
679 $fields = array_slice($fields, 0, $pos, true) +
680 ['status_label' => ['label' => _T('Status label')]] +
681 array_slice($fields, $pos, count($fields) - 1, true);
682 }
683
684 //dynamic fields
685 $member = new Adherent($this->zdb);
686 $member
687 ->disableAllDeps()
688 ->enableDep('dynamics')
689 ->loadFromLoginOrMail($this->login->login);
690 $adh_dynamics = new DynamicFieldsHandle($this->zdb, $this->login, $member);
691
692 $contrib = new Contribution($this->zdb, $this->login);
693 $contrib_dynamics = new DynamicFieldsHandle($this->zdb, $this->login, $contrib);
694
695 //Status
696 $statuts = new Status($this->zdb);
697
698 //Contributions types
699 $ct = new ContributionsTypes($this->zdb);
700
701 //Payment types
702 $ptypes = new PaymentTypes(
703 $this->zdb,
704 $this->preferences,
705 $this->login
706 );
707 $ptlist = $ptypes->getList();
708
709 $filters->setViewCommonsFilters($this->preferences, $this->view);
710
711 $social = new Social($this->zdb);
712 $types = $member->getMemberRegisteredTypes();
713 $social_types = [];
714 foreach ($types as $type) {
715 $social_types[$type] = $social->getSystemType($type);
716 }
717
718 // display page
719 $this->view->render(
720 $response,
721 'pages/advanced_search.html.twig',
722 array(
723 'page_title' => _T("Advanced search"),
724 'filter_groups_options' => $groups_list,
725 'search_fields' => $fields,
726 'adh_dynamics' => $adh_dynamics->getFields(),
727 'contrib_dynamics' => $contrib_dynamics->getFields(),
728 'adh_socials' => $social_types,
729 'statuts' => $statuts->getList(),
730 'contributions_types' => $ct->getList(),
731 'filters' => $filters,
732 'payments_types' => $ptlist
733 )
734 );
735 return $response;
736 }
737
738 /**
739 * Members list for ajax
740 *
741 * @param Request $request PSR Request
742 * @param Response $response PSR Response
743 * @param string|null $option One of 'page' or 'order'
744 * @param string|integer $value Value of the option
745 *
746 * @return Response
747 */
748 public function ajaxList(Request $request, Response $response, string $option = null, $value = null): Response
749 {
750 $post = $request->getParsedBody();
751
752 $filters = $this->session->ajax_members_filters ?? new MembersList();
753
754 if ($option == 'page') {
755 $filters->current_page = (int)$value;
756 }
757
758 //numbers of rows to display
759 if (isset($post['nbshow']) && is_numeric($post['nbshow'])) {
760 $filters->show = (int)$post['nbshow'];
761 }
762
763 $members = new Members($filters);
764 if (!$this->login->isAdmin() && !$this->login->isStaff()) {
765 if ($this->login->isGroupManager()) {
766 $members_list = $members->getManagedMembersList(true);
767 } else {
768 Analog::log(
769 str_replace(
770 ['%id', '%login'],
771 [$this->login->id, $this->login->login],
772 'Trying to list group members without access from #%id (%login)'
773 ),
774 Analog::ERROR
775 );
776 throw new \Exception('Access denied.');
777 }
778 } else {
779 $members_list = $members->getMembersList(true);
780 }
781
782 //assign pagination variables to the template and add pagination links
783 $filters->setViewPagination($this->routeparser, $this->view, false);
784
785 $this->session->ajax_members_filters = $filters;
786
787 $selected_members = null;
788 $unreachables_members = null;
789 if (!isset($post['from'])) {
790 $mailing = $this->session->mailing;
791 if (!isset($post['members'])) {
792 $selected_members = $mailing->recipients;
793 $unreachables_members = $mailing->unreachables;
794 } else {
795 $m = new Members();
796 $selected_members = $m->getArrayList($post['members']);
797 if (isset($post['unreachables']) && is_array($post['unreachables'])) {
798 $unreachables_members = $m->getArrayList($post['unreachables']);
799 }
800 }
801 } else {
802 switch ($post['from']) {
803 case 'groups':
804 if (!isset($post['gid'])) {
805 Analog::log(
806 'Trying to list group members with no group id provided',
807 Analog::ERROR
808 );
809 throw new \Exception('A group id is required.');
810 }
811 if (!isset($post['members'])) {
812 $group = new Group((int)$post['gid']);
813 $selected_members = array();
814 if (!isset($post['mode']) || $post['mode'] == 'members') {
815 $selected_members = $group->getMembers();
816 } elseif ($post['mode'] == 'managers') {
817 $selected_members = $group->getManagers();
818 } else {
819 Analog::log(
820 'Trying to list group members with unknown mode',
821 Analog::ERROR
822 );
823 throw new \Exception('Unknown mode.');
824 }
825 } else {
826 $m = new Members();
827 $selected_members = $m->getArrayList($post['members']);
828 if (isset($post['unreachables']) && is_array($post['unreachables'])) {
829 $unreachables_members = $m->getArrayList($post['unreachables']);
830 }
831 }
832 break;
833 case 'attach':
834 if (!isset($post['id_adh'])) {
835 throw new \RuntimeException(
836 'Current selected member must be excluded while attaching!'
837 );
838 }
839 break;
840 }
841 }
842
843 $params = [
844 'filters' => $filters,
845 'members_list' => $members_list,
846 'selected_members' => $selected_members,
847 'unreachables_members' => $unreachables_members
848 ];
849
850 if (isset($post['multiple'])) {
851 $params['multiple'] = true;
852 }
853
854 if (isset($post['gid'])) {
855 $params['the_id'] = (int)$post['gid'];
856 }
857
858 if (isset($post['id_adh'])) {
859 $params['excluded'] = (int)$post['id_adh'];
860 }
861
862 // display page
863 $this->view->render(
864 $response,
865 'elements/ajax_members.html.twig',
866 $params
867 );
868 return $response;
869 }
870
871 /**
872 * Batch actions handler
873 *
874 * @param Request $request PSR Request
875 * @param Response $response PSR Response
876 *
877 * @return Response
878 */
879 public function handleBatch(Request $request, Response $response): Response
880 {
881 $post = $request->getParsedBody();
882
883 if (isset($post['entries_sel'])) {
884 if (isset($this->session->filter_members)) {
885 $filters = $this->session->filter_members;
886 } else {
887 $filters = new MembersList();
888 }
889
890 $filters->selected = $post['entries_sel'];
891 $this->session->filter_members = $filters;
892
893 if (isset($post['cards'])) {
894 return $response
895 ->withStatus(301)
896 ->withHeader('Location', $this->routeparser->urlFor('pdf-members-cards'));
897 }
898
899 if (isset($post['labels'])) {
900 return $response
901 ->withStatus(301)
902 ->withHeader('Location', $this->routeparser->urlFor('pdf-members-labels'));
903 }
904
905 if (isset($post['sendmail'])) {
906 return $response
907 ->withStatus(301)
908 ->withHeader('Location', $this->routeparser->urlFor('mailing') . '?mailing_new=new');
909 }
910
911 if (isset($post['attendance_sheet'])) {
912 return $response
913 ->withStatus(301)
914 ->withHeader('Location', $this->routeparser->urlFor('attendance_sheet_details'));
915 }
916
917 if (isset($post['csv'])) {
918 return $response
919 ->withStatus(301)
920 ->withHeader('Location', $this->routeparser->urlFor('csv-memberslist'));
921 }
922
923 if (isset($post['delete'])) {
924 return $response
925 ->withStatus(301)
926 ->withHeader('Location', $this->routeparser->urlFor('removeMembers'));
927 }
928
929 if (isset($post['masschange'])) {
930 return $response
931 ->withStatus(301)
932 ->withHeader('Location', $this->routeparser->urlFor('masschangeMembers'));
933 }
934
935 if (isset($post['masscontributions'])) {
936 return $response
937 ->withStatus(301)
938 ->withHeader('Location', $this->routeparser->urlFor('massAddContributionsChooseType'));
939 }
940
941 throw new \RuntimeException('Does not know what to batch :(');
942 } else {
943 $this->flash->addMessage(
944 'error_detected',
945 _T("No member was selected, please check at least one name.")
946 );
947
948 return $response
949 ->withStatus(301)
950 ->withHeader('Location', $this->routeparser->urlFor('members'));
951 }
952 }
953
954 // /CRUD - Read
955 // CRUD - Update
956
957 /**
958 * Edit page
959 *
960 * @param Request $request PSR Request
961 * @param Response $response PSR Response
962 * @param integer $id Member id/array of members id
963 * @param string $action null or 'add'
964 *
965 * @return Response
966 */
967 public function edit(
968 Request $request,
969 Response $response,
970 int $id = null,
971 string $action = 'edit'
972 ): Response {
973 //instantiate member object
974 $member = new Adherent($this->zdb);
975
976 if ($this->session->member !== null) {
977 //retrieve from session, in add or edit
978 $member = $this->session->member;
979 $this->session->member = null;
980 $id = $member->id;
981 }
982 $member->enableAllDeps();
983
984 if ($id !== null) {
985 //load requested member
986 $member->load($id);
987 $can = $member->canEdit($this->login);
988 } else {
989 $can = $member->canCreate($this->login);
990 }
991
992 if (!$can) {
993 $this->flash->addMessage(
994 'error_detected',
995 _T("You do not have permission for requested URL.")
996 );
997
998 return $response
999 ->withHeader(
1000 'Location',
1001 $this->routeparser->urlFor('me')
1002 );
1003 }
1004
1005 //if adding a child, force parent here
1006 if ($action === 'addchild') {
1007 $member->setParent((int)$this->login->id);
1008 }
1009
1010 // flagging required fields
1011 $fc = $this->fields_config;
1012
1013 // password required if we create a new member
1014 if ($id !== null) {
1015 $fc->setNotRequired('mdp_adh');
1016 }
1017
1018 //handle requirements for parent fields
1019 $parent_fields = $member->getParentFields();
1020 $tpl_parent_fields = []; //for JS when detaching
1021 foreach ($parent_fields as $field) {
1022 if ($fc->isRequired($field)) {
1023 $tpl_parent_fields[] = $field;
1024 if ($member->hasParent()) {
1025 $fc->setNotRequired($field);
1026 }
1027 }
1028 }
1029
1030 // flagging required fields invisible to members
1031 if ($this->login->isAdmin() || $this->login->isStaff()) {
1032 $fc->setNotRequired('activite_adh');
1033 $fc->setNotRequired('id_statut');
1034 }
1035
1036 // template variable declaration
1037 $title = _T("Member Profile");
1038 if ($member->id != '') {
1039 $title .= ' (' . _T("modification") . ')';
1040 } else {
1041 $title .= ' (' . _T("creation") . ')';
1042 }
1043
1044 //Status
1045 $statuts = new Status($this->zdb);
1046 //Titles
1047 $titles = new Titles($this->zdb);
1048
1049 //Groups
1050 $groups = new Groups($this->zdb, $this->login);
1051 $groups_list = $groups->getSimpleList(true);
1052
1053 $form_elements = $fc->getFormElements(
1054 $this->login,
1055 $id === null
1056 );
1057
1058 // members
1059 $m = new Members();
1060 $pid = null;
1061 if ($member->hasParent()) {
1062 $pid = ($member->parent instanceof Adherent ? $member->parent->id : $member->parent);
1063 }
1064 $members = $m->getDropdownMembers(
1065 $this->zdb,
1066 $this->login,
1067 $pid
1068 );
1069
1070 $route_params['members'] = [
1071 'filters' => $m->getFilters(),
1072 'count' => $m->getCount()
1073 ];
1074
1075 if (count($members)) {
1076 $route_params['members']['list'] = $members;
1077 }
1078
1079 if ($action === 'edit') {
1080 $route_params['navigate'] = $this->handleNavigationLinks($member->id);
1081 }
1082
1083 // display page
1084 $this->view->render(
1085 $response,
1086 'pages/member_form.html.twig',
1087 array(
1088 'parent_tpl' => 'page.html.twig',
1089 'autocomplete' => true,
1090 'page_title' => $title,
1091 'member' => $member,
1092 'self_adh' => false,
1093 // pseudo random int
1094 'time' => time(),
1095 'titles_list' => $titles->getList(),
1096 'statuts' => $statuts->getList(),
1097 'groups' => $groups_list,
1098 'fieldsets' => $form_elements['fieldsets'],
1099 'hidden_elements' => $form_elements['hiddens'],
1100 'parent_fields' => $tpl_parent_fields,
1101 'addchild' => ($action === 'addchild'),
1102 'osocials' => new Social($this->zdb)
1103 ) + $route_params
1104 );
1105 return $response;
1106 }
1107
1108 /**
1109 * Edit action
1110 *
1111 * @param Request $request PSR Request
1112 * @param Response $response PSR Response
1113 * @param integer $id Member id
1114 *
1115 * @return Response
1116 */
1117 public function doEdit(Request $request, Response $response, int $id): Response
1118 {
1119 return $this->store($request, $response);
1120 }
1121
1122 /**
1123 * Massive change page
1124 *
1125 * @param Request $request PSR Request
1126 * @param Response $response PSR Response
1127 *
1128 * @return Response
1129 */
1130 public function massChange(Request $request, Response $response): Response
1131 {
1132 $filters = $this->session->filter_members;
1133
1134 $data = [
1135 'id' => $filters->selected,
1136 'redirect_uri' => $this->routeparser->urlFor('members')
1137 ];
1138
1139 $fc = $this->fields_config;
1140 $form_elements = $fc->getMassiveFormElements($this->members_fields, $this->login);
1141
1142 //dynamic fields
1143 $member = new Adherent($this->zdb);
1144 $member->disableAllDeps()->enableDep('dynamics');
1145
1146 //Status
1147 $statuts = new Status($this->zdb);
1148 //Titles
1149 $titles = new Titles($this->zdb);
1150
1151 // display page
1152 $this->view->render(
1153 $response,
1154 'modals/mass_change_members.html.twig',
1155 array(
1156 'mode' => ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') ? 'ajax' : '',
1157 'page_title' => str_replace(
1158 '%count',
1159 count($data['id']),
1160 _T('Mass change %count members')
1161 ),
1162 'form_url' => $this->routeparser->urlFor('masschangeMembersReview'),
1163 'cancel_uri' => $this->routeparser->urlFor('members'),
1164 'data' => $data,
1165 'member' => $member,
1166 'fieldsets' => $form_elements['fieldsets'],
1167 'titles_list' => $titles->getList(),
1168 'statuts' => $statuts->getList(),
1169 'require_mass' => true
1170 )
1171 );
1172 return $response;
1173 }
1174
1175 /**
1176 * Massive changes validation page
1177 *
1178 * @param Request $request PSR Request
1179 * @param Response $response PSR Response
1180 *
1181 * @return Response
1182 */
1183 public function validateMassChange(Request $request, Response $response): Response
1184 {
1185 $post = $request->getParsedBody();
1186 $changes = [];
1187
1188 if (!isset($post['confirm'])) {
1189 $this->flash->addMessage(
1190 'error_detected',
1191 _T("Mass changes has not been confirmed!")
1192 );
1193 } else {
1194 //we want only visibles fields
1195 $fc = $this->fields_config;
1196 $form_elements = $fc->getMassiveFormElements($this->members_fields, $this->login);
1197
1198 foreach ($form_elements['fieldsets'] as $form_element) {
1199 foreach ($form_element->elements as $field) {
1200 if (
1201 isset($post['mass_' . $field->field_id])
1202 && (isset($post[$field->field_id]) || $field->type === FieldsConfig::TYPE_BOOL)
1203 ) {
1204 $changes[$field->field_id] = [
1205 'label' => $field->label,
1206 'value' => $post[$field->field_id] ?? 0
1207 ];
1208 }
1209 }
1210 }
1211
1212 //handle dynamic fields
1213 $member = new Adherent($this->zdb);
1214 $member
1215 ->enableAllDeps()
1216 ->setDependencies(
1217 $this->preferences,
1218 $this->members_fields,
1219 $this->history
1220 );
1221 $dynamic_fields = $member->getDynamicFields()->getFields();
1222 foreach ($dynamic_fields as $field) {
1223 $mass_id = 'mass_info_field_' . $field->getId();
1224 $field_id = 'info_field_' . $field->getId() . '_1';
1225 if (
1226 isset($post[$mass_id])
1227 && (isset($post[$field_id]) || $field instanceof Boolean)
1228 ) {
1229 $changes[$field_id] = [
1230 'label' => $field->getName(),
1231 'value' => $post[$field_id] ?? 0
1232 ];
1233 }
1234 }
1235 }
1236
1237 $filters = $this->session->filter_members;
1238 $data = [
1239 'id' => $filters->selected,
1240 'redirect_uri' => $this->routeparser->urlFor('members')
1241 ];
1242
1243 //Status
1244 $statuts = new Status($this->zdb);
1245 //Titles
1246 $titles = new Titles($this->zdb);
1247
1248 // display page
1249 $this->view->render(
1250 $response,
1251 'modals/mass_change_members.html.twig',
1252 array(
1253 'mode' => ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') ? 'ajax' : '',
1254 'page_title' => str_replace(
1255 '%count',
1256 count($data['id']),
1257 _T('Review mass change %count members')
1258 ),
1259 'form_url' => $this->routeparser->urlFor('massstoremembers'),
1260 'cancel_uri' => $this->routeparser->urlFor('members'),
1261 'data' => $data,
1262 'titles_list' => $titles->getList(),
1263 'statuts' => $statuts->getList(),
1264 'changes' => $changes
1265 )
1266 );
1267 return $response;
1268 }
1269
1270 /**
1271 * Do massive changes
1272 *
1273 * @param Request $request PSR Request
1274 * @param Response $response PSR Response
1275 *
1276 * @return Response
1277 */
1278 public function doMassChange(Request $request, Response $response): Response
1279 {
1280 $post = $request->getParsedBody();
1281 $redirect_url = $post['redirect_uri'];
1282 $error_detected = [];
1283 $mass = 0;
1284 $dynamic_fields = null;
1285
1286 unset($post['redirect_uri']);
1287 if (!isset($post['confirm'])) {
1288 $error_detected[] = _T("Mass changes has not been confirmed!");
1289 } else {
1290 unset($post['confirm']);
1291 $ids = $post['id'];
1292 unset($post['id']);
1293
1294 $fc = $this->fields_config;
1295 $form_elements = $fc->getMassiveFormElements($this->members_fields, $this->login);
1296 $disabled = $this->members_fields;
1297 foreach (array_keys($post) as $key) {
1298 $found = false;
1299 foreach ($form_elements['fieldsets'] as $fieldset) {
1300 if (isset($fieldset->elements[$key])) {
1301 $found = true;
1302 break;
1303 }
1304 }
1305
1306 if (!$found) {
1307 //try on dynamic fields
1308 if ($dynamic_fields === null) {
1309 //handle dynamic fields
1310 $member = new Adherent($this->zdb);
1311 $member
1312 ->enableAllDeps()
1313 ->setDependencies(
1314 $this->preferences,
1315 $this->members_fields,
1316 $this->history
1317 );
1318 $dynamic_fields = $member->getDynamicFields()->getFields();
1319 }
1320 foreach ($dynamic_fields as $field) {
1321 $field_id = 'info_field_' . $field->getId() . '_1';
1322 if ($key == $field_id) {
1323 $found = true;
1324 break;
1325 }
1326 }
1327 }
1328
1329 if (!$found) {
1330 Analog::log(
1331 'Permission issue mass editing field ' . $key,
1332 Analog::WARNING
1333 );
1334 unset($post[$key]);
1335 } else {
1336 unset($disabled[$key]);
1337 }
1338 }
1339
1340 if (!count($post)) {
1341 $error_detected[] = _T("Nothing to do!");
1342 } else {
1343 foreach ($ids as $id) {
1344 $is_manager = !$this->login->isAdmin()
1345 && !$this->login->isStaff()
1346 && $this->login->isGroupManager();
1347 $member = new Adherent($this->zdb);
1348 $member->disableAllDeps();
1349 if ($is_manager) {
1350 $member->enableDep('groups');
1351 }
1352 $member->load((int)$id);
1353 $member->setDependencies(
1354 $this->preferences,
1355 $this->members_fields,
1356 $this->history
1357 );
1358 if (!$member->canEdit($this->login)) {
1359 continue;
1360 }
1361
1362 $valid = $member->check($post, [], $disabled);
1363 if ($valid === true) {
1364 $done = $member->store();
1365 if (!$done) {
1366 $error_detected[] = _T("An error occurred while storing the member.");
1367 } else {
1368 ++$mass;
1369 }
1370 } else {
1371 $error_detected = array_merge($error_detected, $valid);
1372 }
1373 }
1374 }
1375 }
1376
1377 if ($mass == 0 && !count($error_detected)) {
1378 $error_detected[] = _T('Something went wront during mass edition!');
1379 } else {
1380 $this->flash->addMessage(
1381 'success_detected',
1382 str_replace(
1383 '%count',
1384 $mass,
1385 _T('%count members has been changed successfully!')
1386 )
1387 );
1388 }
1389
1390 if (count($error_detected) > 0) {
1391 foreach ($error_detected as $error) {
1392 $this->flash->addMessage(
1393 'error_detected',
1394 $error
1395 );
1396 }
1397 }
1398
1399 if (!($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest')) {
1400 return $response
1401 ->withStatus(301)
1402 ->withHeader('Location', $redirect_url);
1403 } else {
1404 return $this->withJson(
1405 $response,
1406 [
1407 'success' => count($error_detected) === 0
1408 ]
1409 );
1410 }
1411 }
1412
1413 /**
1414 * Store
1415 *
1416 * @param Request $request PSR Request
1417 * @param Response $response PSR Response
1418 *
1419 * @return Response
1420 */
1421 public function store(Request $request, Response $response): Response
1422 {
1423 if (!$this->preferences->pref_bool_selfsubscribe && !$this->login->isLogged()) {
1424 return $response
1425 ->withStatus(301)
1426 ->withHeader('Location', $this->routeparser->urlFor('slash'));
1427 }
1428
1429 $post = $request->getParsedBody();
1430 $member = new Adherent($this->zdb);
1431 $member
1432 ->enableAllDeps()
1433 ->setDependencies(
1434 $this->preferences,
1435 $this->members_fields,
1436 $this->history
1437 );
1438
1439 $success_detected = [];
1440 $warning_detected = [];
1441 $error_detected = [];
1442
1443 if ($this->isSelfMembership() && !isset($post[Adherent::PK])) {
1444 //mark as self membership
1445 $member->setSelfMembership();
1446
1447 //check captcha
1448 $gaptcha = $this->session->gaptcha;
1449 if (!$gaptcha->check($post['gaptcha'])) {
1450 $error_detected[] = _T('Invalid captcha');
1451 }
1452 }
1453
1454 // new or edit
1455 if (isset($post['id_adh'])) {
1456 $member->load((int)$post['id_adh']);
1457 if (!$member->canEdit($this->login)) {
1458 //redirection should have been done before. Just throw an Exception.
1459 throw new \RuntimeException(
1460 str_replace(
1461 '%id',
1462 $member->id,
1463 'No right to store member #%id'
1464 )
1465 );
1466 }
1467 } else {
1468 if ($member->id != '') {
1469 $member->load($this->login->id);
1470 }
1471 }
1472
1473 // flagging required fields
1474 $fc = $this->fields_config;
1475
1476 // password required if we create a new member but not from self subscription
1477 if ($member->id != '' || $this->isSelfMembership()) {
1478 $fc->setNotRequired('mdp_adh');
1479 }
1480
1481 if (
1482 $member->hasParent() && !isset($post['detach_parent'])
1483 || isset($post['parent_id']) && !empty($post['parent_id'])
1484 ) {
1485 $parent_fields = $member->getParentFields();
1486 foreach ($parent_fields as $field) {
1487 if ($fc->isRequired($field)) {
1488 $fc->setNotRequired($field);
1489 }
1490 }
1491 }
1492
1493 // flagging required fields invisible to members
1494 if ($this->login->isAdmin() || $this->login->isStaff()) {
1495 $fc->setNotRequired('activite_adh');
1496 $fc->setNotRequired('id_statut');
1497 }
1498
1499 $form_elements = $fc->getFormElements(
1500 $this->login,
1501 $member->id == '',
1502 $this->isSelfMembership()
1503 );
1504 $fieldsets = $form_elements['fieldsets'];
1505 $required = array();
1506 $disabled = array();
1507
1508 foreach ($fieldsets as $category) {
1509 foreach ($category->elements as $field) {
1510 if ($field->required == true) {
1511 $required[$field->field_id] = true;
1512 }
1513 if ($field->disabled == true) {
1514 $disabled[$field->field_id] = true;
1515 } elseif (!isset($post[$field->field_id])) {
1516 switch ($field->field_id) {
1517 //unchecked booleans are not sent from form
1518 case 'bool_admin_adh':
1519 case 'bool_exempt_adh':
1520 case 'bool_display_info':
1521 $post[$field->field_id] = 0;
1522 break;
1523 }
1524 }
1525 }
1526 }
1527
1528 $real_requireds = array_diff(array_keys($required), array_keys($disabled));
1529
1530 // send email to member
1531 if ($this->isSelfMembership() || isset($post['mail_confirm']) && $post['mail_confirm'] == '1') {
1532 $member->setSendmail(); //flag to send creation email
1533 }
1534
1535 // Validation
1536 $redirect_url = $this->routeparser->urlFor('member', ['id' => $member->id]);
1537 if (!count($real_requireds) || isset($post[array_shift($real_requireds)])) {
1538 // regular fields
1539 $valid = $member->check($post, $required, $disabled);
1540 if ($valid !== true) {
1541 $error_detected = array_merge($error_detected, $valid);
1542 }
1543
1544 if (count($error_detected) == 0) {
1545 //all goes well, we can proceed
1546
1547 $new = false;
1548 if ($member->id == '') {
1549 $new = true;
1550 }
1551
1552 $store = $member->store();
1553 if ($store === true) {
1554 //member has been stored :)
1555 if ($new) {
1556 if ($this->isSelfMembership()) {
1557 $success_detected[] = _T("Your account has been created!");
1558 if (
1559 $this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED
1560 && $member->getEmail() != ''
1561 ) {
1562 $success_detected[] = _T("An email has been sent to you, check your inbox.");
1563 }
1564 } else {
1565 $success_detected[] = _T("New member has been successfully added.");
1566 }
1567 } else {
1568 $success_detected[] = _T("Member account has been modified.");
1569 }
1570
1571 if ($this->login->isGroupManager()) {
1572 //add/remove user from groups
1573 $groups_adh = $post['groups_adh'] ?? null;
1574 $add_groups = Groups::addMemberToGroups(
1575 $member,
1576 $groups_adh
1577 );
1578
1579 if ($add_groups === false) {
1580 $error_detected[] = _T("An error occurred adding member to its groups.");
1581 }
1582 }
1583 if ($this->login->isSuperAdmin() || $this->login->isAdmin() || $this->login->isStaff()) {
1584 //add/remove manager from groups
1585 $managed_groups_adh = $post['groups_managed_adh'] ?? null;
1586 $add_groups = Groups::addMemberToGroups(
1587 $member,
1588 $managed_groups_adh,
1589 true
1590 );
1591 $member->loadGroups();
1592
1593 if ($add_groups === false) {
1594 $error_detected[] = _T("An error occurred adding member to its groups as manager.");
1595 }
1596 }
1597 } else {
1598 //something went wrong :'(
1599 $error_detected[] = _T("An error occurred while storing the member.");
1600 }
1601 }
1602
1603 if (count($error_detected) === 0) {
1604 $files_res = $member->handleFiles($_FILES);
1605 if (is_array($files_res)) {
1606 $error_detected = array_merge($error_detected, $files_res);
1607 }
1608
1609 if (isset($post['del_photo'])) {
1610 if (!$member->picture->delete($member->id)) {
1611 $error_detected[] = _T("Delete failed");
1612 $str_adh = $member->id . ' (' . $member->sname . ' ' . ')';
1613 Analog::log(
1614 'Unable to delete picture for member ' . $str_adh,
1615 Analog::ERROR
1616 );
1617 }
1618 }
1619 }
1620
1621 if (count($error_detected) > 0) {
1622 foreach ($error_detected as $error) {
1623 if (strpos($error, '%member_url_') !== false) {
1624 preg_match('/%member_url_(\d+)/', $error, $matches);
1625 $url = $this->routeparser->urlFor('member', ['id' => $matches[1]]);
1626 $error = str_replace(
1627 '%member_url_' . $matches[1],
1628 $url,
1629 $error
1630 );
1631 }
1632 $this->flash->addMessage(
1633 'error_detected',
1634 $error
1635 );
1636 }
1637 }
1638
1639 if (count($warning_detected) > 0) {
1640 foreach ($warning_detected as $warning) {
1641 $this->flash->addMessage(
1642 'warning_detected',
1643 $warning
1644 );
1645 }
1646 }
1647 if (count($success_detected) > 0) {
1648 foreach ($success_detected as $success) {
1649 $this->flash->addMessage(
1650 'success_detected',
1651 $success
1652 );
1653 }
1654 }
1655
1656 if (count($error_detected) === 0) {
1657 if ($this->isSelfMembership()) {
1658 $redirect_url = $this->routeparser->urlFor('login');
1659 } elseif (
1660 isset($post['redirect_on_create'])
1661 && $post['redirect_on_create'] > Adherent::AFTER_ADD_DEFAULT
1662 ) {
1663 switch ($post['redirect_on_create']) {
1664 case Adherent::AFTER_ADD_TRANS:
1665 $redirect_url = $this->routeparser->urlFor('addTransaction');
1666 break;
1667 case Adherent::AFTER_ADD_NEW:
1668 $redirect_url = $this->routeparser->urlFor('addMember');
1669 break;
1670 case Adherent::AFTER_ADD_SHOW:
1671 $redirect_url = $this->routeparser->urlFor('member', ['id' => $member->id]);
1672 break;
1673 case Adherent::AFTER_ADD_LIST:
1674 $redirect_url = $this->routeparser->urlFor('members');
1675 break;
1676 case Adherent::AFTER_ADD_HOME:
1677 $redirect_url = $this->routeparser->urlFor('slash');
1678 break;
1679 }
1680 } elseif (!isset($post['id_adh']) && !$member->isDueFree()) {
1681 $redirect_url = $this->routeparser->urlFor(
1682 'addContribution',
1683 ['type' => 'fee']
1684 ) . '?id_adh=' . $member->id;
1685 } else {
1686 $redirect_url = $this->routeparser->urlFor('member', ['id' => $member->id]);
1687 }
1688 } else {
1689 //store entity in session
1690 $this->session->member = $member;
1691
1692 if ($this->isSelfMembership()) {
1693 $redirect_url = $this->routeparser->urlFor('subscribe');
1694 } else {
1695 if ($member->id) {
1696 $redirect_url = $this->routeparser->urlFor(
1697 'editMember',
1698 ['id' => $member->id]
1699 );
1700 } else {
1701 $redirect_url = $this->routeparser->urlFor((isset($post['addchild']) ? 'addMemberChild' : 'addMember'));
1702 }
1703 }
1704 }
1705 }
1706
1707 return $response
1708 ->withStatus(301)
1709 ->withHeader('Location', $redirect_url);
1710 }
1711
1712
1713 // /CRUD - Update
1714 // CRUD - Delete
1715
1716 /**
1717 * Get redirection URI
1718 *
1719 * @param array $args Route arguments
1720 *
1721 * @return string
1722 */
1723 public function redirectUri(array $args)
1724 {
1725 return $this->routeparser->urlFor('members');
1726 }
1727
1728 /**
1729 * Get form URI
1730 *
1731 * @param array $args Route arguments
1732 *
1733 * @return string
1734 */
1735 public function formUri(array $args)
1736 {
1737 return $this->routeparser->urlFor(
1738 'doRemoveMember',
1739 $args
1740 );
1741 }
1742
1743 /**
1744 * Get confirmation removal page title
1745 *
1746 * @param array $args Route arguments
1747 *
1748 * @return string
1749 */
1750 public function confirmRemoveTitle(array $args)
1751 {
1752 if (isset($args['id_adh']) || isset($args['id'])) {
1753 //one member removal
1754 $id_adh = $args['id_adh'] ?? $args['id'];
1755 $adh = new Adherent($this->zdb, (int)$id_adh);
1756 return sprintf(
1757 _T('Remove member %1$s'),
1758 $adh->sfullname
1759 );
1760 } else {
1761 //batch members removal
1762 $filters = $this->session->filter_members;
1763 return str_replace(
1764 '%count',
1765 count($filters->selected),
1766 _T('You are about to remove %count members.')
1767 );
1768 }
1769 }
1770
1771 /**
1772 * Remove object
1773 *
1774 * @param array $args Route arguments
1775 * @param array $post POST values
1776 *
1777 * @return bool
1778 */
1779 protected function doDelete(array $args, array $post)
1780 {
1781 if (isset($this->session->filter_members)) {
1782 $filters = $this->session->filter_members;
1783 } else {
1784 $filters = new MembersList();
1785 }
1786 $members = new Members($filters);
1787
1788 if (!is_array($post['id'])) {
1789 $ids = (array)$post['id'];
1790 } else {
1791 $ids = $post['id'];
1792 }
1793
1794 return $members->removeMembers($ids);
1795 }
1796
1797 // CRUD - Delete
1798
1799 /**
1800 * Set self memebrship flag
1801 *
1802 * @return MembersController
1803 */
1804 private function setSelfMembership(): MembersController
1805 {
1806 $this->is_self_membership = true;
1807 return $this;
1808 }
1809
1810 /**
1811 * Is self membership?
1812 *
1813 * @return bool
1814 */
1815 private function isSelfMembership(): bool
1816 {
1817 return $this->is_self_membership;
1818 }
1819
1820 /**
1821 * Handle navigation links
1822 *
1823 * @param int $id_adh Current member ID
1824 *
1825 * @return array
1826 */
1827 private function handleNavigationLinks(int $id_adh): array
1828 {
1829 $navigate = array();
1830
1831 if (isset($this->session->filter_members)) {
1832 $filters = $this->session->filter_members;
1833 } else {
1834 $filters = new MembersList();
1835 }
1836 //we must navigate between all members
1837 $filters->show = 0;
1838
1839 if (
1840 $this->login->isAdmin()
1841 || $this->login->isStaff()
1842 || $this->login->isGroupManager()
1843 ) {
1844 $m = new Members($filters);
1845
1846 $ids = array();
1847 $fields = [Adherent::PK, 'nom_adh', 'prenom_adh'];
1848 if ($this->login->isAdmin() || $this->login->isStaff()) {
1849 $ids = $m->getMembersList(false, $fields);
1850 } else {
1851 $ids = $m->getManagedMembersList(false, $fields);
1852 }
1853
1854 $ids = $ids->toArray();
1855 foreach ($ids as $k => $m) {
1856 if ($m['id_adh'] == $id_adh) {
1857 $navigate = array(
1858 'cur' => $m['id_adh'],
1859 'count' => $filters->counter,
1860 'pos' => $k + 1
1861 );
1862 if ($k > 0) {
1863 $navigate['prev'] = $ids[$k - 1]['id_adh'];
1864 }
1865 if ($k < count($ids) - 1) {
1866 $navigate['next'] = $ids[$k + 1]['id_adh'];
1867 }
1868 break;
1869 }
1870 }
1871 }
1872
1873 return $navigate;
1874 }
1875
1876 /**
1877 * Get filter name in session
1878 *
1879 * @param array|null $args Route arguments
1880 *
1881 * @return string
1882 */
1883 public function getFilterName(array $args = null): string
1884 {
1885 return 'filter_members';
1886 }
1887 }