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