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