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