]> git.agnieray.net Git - galette.git/blob - galette/includes/routes/members.routes.php
1b829603ec76cf47dd357955e22f5c2d1be62236
[galette.git] / galette / includes / routes / members.routes.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Members related routes
7 *
8 * PHP version 5
9 *
10 * Copyright © 2014-2018 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 Routes
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2014-2018 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 * @version SVN: $Id$
34 * @link http://galette.tuxfamily.org
35 * @since 0.8.2dev 2014-11-27
36 */
37
38 use Analog\Analog;
39 use Galette\Entity\DynamicFieldsHandle;
40 use Galette\Core\Password;
41 use Galette\Core\PasswordImage;
42 use Galette\Core\Mailing;
43 use Galette\Core\GaletteMail;
44 use Galette\Repository\Members;
45 use Galette\Filters\MembersList;
46 use Galette\Filters\SavedSearchesList;
47 use Galette\Filters\AdvancedMembersList;
48 use Galette\Entity\FieldsConfig;
49 use Galette\Entity\Contribution;
50 use Galette\Repository\Groups;
51 use Galette\Repository\Reminders;
52 use Galette\Entity\Adherent;
53 use Galette\IO\PdfMembersCards;
54 use Galette\IO\PdfMembersLabels;
55 use Galette\IO\Csv;
56 use Galette\IO\CsvOut;
57 use Galette\Entity\Status;
58 use Galette\Repository\Titles;
59 use Galette\Entity\Texts;
60 use Galette\IO\Pdf;
61 use Galette\Core\MailingHistory;
62 use Galette\Entity\Group;
63 use Galette\IO\File;
64 use Galette\Core\Authentication;
65 use Galette\Repository\PaymentTypes;
66 use Galette\Repository\SavedSearches;
67
68 //self subscription
69 $app->get(
70 '/subscribe',
71 function ($request, $response) {
72 if (!$this->preferences->pref_bool_selfsubscribe || $this->login->isLogged()) {
73 return $response
74 ->withStatus(301)
75 ->withHeader('Location', $this->router->pathFor('slash'));
76 }
77
78 if ($this->session->member !== null) {
79 $member = $this->session->member;
80 $this->session->member = null;
81 } else {
82 $deps = [
83 'dynamics' => true
84 ];
85 $member = new Adherent($this->zdb, null, $deps);
86 }
87
88 //mark as self membership
89 $member->setSelfMembership();
90
91 // flagging required fields
92 $fc = $this->fields_config;
93 $form_elements = $fc->getFormElements($this->login, true, true);
94
95 //image to defeat mass filling forms
96 $spam = new PasswordImage();
97 $spam_pass = $spam->newImage();
98 $spam_img = $spam->getImage();
99
100 // members
101 $members = [];
102 $m = new Members();
103 $required_fields = array(
104 'id_adh',
105 'nom_adh',
106 'prenom_adh'
107 );
108 $list_members = $m->getList(false, $required_fields, true);
109
110 if (count($list_members) > 0) {
111 foreach ($list_members as $lmember) {
112 $pk = Adherent::PK;
113 $sname = mb_strtoupper($lmember->nom_adh, 'UTF-8') .
114 ' ' . ucwords(mb_strtolower($lmember->prenom_adh, 'UTF-8')) .
115 ' (' . $lmember->id_adh . ')';
116 $members[$lmember->$pk] = $sname;
117 }
118 }
119
120 $params['members'] = [
121 'filters' => $m->getFilters(),
122 'count' => $m->getCount()
123 ];
124
125 //check if current attached member is part of the list
126 if ($member->hasParent()) {
127 if (!isset($members[$member->parent->id])) {
128 $members =
129 [$member->parent->id => $member->parent->getSName()] +
130 $members
131 ;
132 }
133 }
134
135 if (count($members)) {
136 $params['members']['list'] = $members;
137 }
138
139 // display page
140 $this->view->render(
141 $response,
142 'member.tpl',
143 array(
144 'page_title' => _T("Subscription"),
145 'parent_tpl' => 'public_page.tpl',
146 'member' => $member,
147 'self_adh' => true,
148 'require_calendar' => true,
149 'autocomplete' => true,
150 // pseudo random int
151 'time' => time(),
152 'titles_list' => Titles::getList($this->zdb),
153 //self_adh specific
154 'spam_pass' => $spam_pass,
155 'spam_img' => $spam_img,
156 'fieldsets' => $form_elements['fieldsets'],
157 'hidden_elements' => $form_elements['hiddens']
158 ) + $params
159 );
160 return $response;
161 }
162 )->setName('subscribe');
163
164 //members list CSV export
165 $app->get(
166 '/members/export/csv',
167 function ($request, $response) {
168 $csv = new CsvOut();
169
170 if (isset($this->session->filter_members)) {
171 //CAUTION: this one may be simple or advanced, display must change
172 $filters = $this->session->filter_members;
173 } else {
174 $filters = new MembersList();
175 }
176
177 $export_fields = null;
178 if (file_exists(GALETTE_CONFIG_PATH . 'local_export_fields.inc.php')) {
179 include_once GALETTE_CONFIG_PATH . 'local_export_fields.inc.php';
180 $export_fields = $fields;
181 }
182
183 // fields visibility
184 $fc = $this->fields_config;
185 $visibles = $fc->getVisibilities();
186 //hack for id_adh and parent_id
187 $hacks = ['id_adh', 'parent_id'];
188 foreach ($hacks as $hack) {
189 if ($visibles[$hack] == FieldsConfig::NOBODY) {
190 $visibles[$hack] = FieldsConfig::MANAGER;
191 }
192 }
193 $access_level = $this->login->getAccessLevel();
194 $fields = array();
195 $labels = array();
196 foreach ($this->members_fields as $k => $f) {
197 // skip fields blacklisted for export
198 if ($k === 'mdp_adh' ||
199 ($export_fields !== null &&
200 (is_array($export_fields) && !in_array($k, $export_fields)))
201 ) {
202 continue;
203 }
204
205 // skip fields according to access control
206 if ($visibles[$k] == FieldsConfig::NOBODY ||
207 ($visibles[$k] == FieldsConfig::ADMIN &&
208 $access_level < Authentication::ACCESS_ADMIN) ||
209 ($visibles[$k] == FieldsConfig::STAFF &&
210 $access_level < Authentication::ACCESS_STAFF) ||
211 ($visibles[$k] == FieldsConfig::MANAGER &&
212 $access_level < Authentication::ACCESS_MANAGER)
213 ) {
214 continue;
215 }
216
217 $fields[] = $k;
218 $labels[] = $f['label'];
219 }
220
221 $members = new Members($filters);
222 $members_list = $members->getArrayList(
223 $filters->selected,
224 null,
225 false,
226 false,
227 $fields,
228 true
229 );
230
231 $s = new Status($this->zdb);
232 $statuses = $s->getList();
233
234 $t = new Titles();
235 $titles = $t->getList($this->zdb);
236
237 foreach ($members_list as &$member) {
238 if (isset($member->id_statut)) {
239 //add textual status
240 $member->id_statut = $statuses[$member->id_statut];
241 }
242
243 if (isset($member->titre_adh)) {
244 //add textuel title
245 $member->titre_adh = $titles[$member->titre_adh]->short;
246 }
247
248 //handle dates
249 if (isset($member->date_crea_adh)) {
250 if ($member->date_crea_adh != ''
251 && $member->date_crea_adh != '1901-01-01'
252 ) {
253 $dcrea = new DateTime($member->date_crea_adh);
254 $member->date_crea_adh = $dcrea->format(__("Y-m-d"));
255 } else {
256 $member->date_crea_adh = '';
257 }
258 }
259
260 if (isset($member->date_modif_adh)) {
261 if ($member->date_modif_adh != ''
262 && $member->date_modif_adh != '1901-01-01'
263 ) {
264 $dmodif = new DateTime($member->date_modif_adh);
265 $member->date_modif_adh = $dmodif->format(__("Y-m-d"));
266 } else {
267 $member->date_modif_adh = '';
268 }
269 }
270
271 if (isset($member->date_echeance)) {
272 if ($member->date_echeance != ''
273 && $member->date_echeance != '1901-01-01'
274 ) {
275 $dech = new DateTime($member->date_echeance);
276 $member->date_echeance = $dech->format(__("Y-m-d"));
277 } else {
278 $member->date_echeance = '';
279 }
280 }
281
282 if (isset($member->ddn_adh)) {
283 if ($member->ddn_adh != ''
284 && $member->ddn_adh != '1901-01-01'
285 ) {
286 $ddn = new DateTime($member->ddn_adh);
287 $member->ddn_adh = $ddn->format(__("Y-m-d"));
288 } else {
289 $member->ddn_adh = '';
290 }
291 }
292
293 if (isset($member->sexe_adh)) {
294 //handle gender
295 switch ($member->sexe_adh) {
296 case Adherent::MAN:
297 $member->sexe_adh = _T("Man");
298 break;
299 case Adherent::WOMAN:
300 $member->sexe_adh = _T("Woman");
301 break;
302 case Adherent::NC:
303 $member->sexe_adh = _T("Unspecified");
304 break;
305 }
306 }
307
308 //handle booleans
309 if (isset($member->activite_adh)) {
310 $member->activite_adh
311 = ($member->activite_adh) ? _T("Yes") : _T("No");
312 }
313 if (isset($member->bool_admin_adh)) {
314 $member->bool_admin_adh
315 = ($member->bool_admin_adh) ? _T("Yes") : _T("No");
316 }
317 if (isset($member->bool_exempt_adh)) {
318 $member->bool_exempt_adh
319 = ($member->bool_exempt_adh) ? _T("Yes") : _T("No");
320 }
321 if (isset($member->bool_display_info)) {
322 $member->bool_display_info
323 = ($member->bool_display_info) ? _T("Yes") : _T("No");
324 }
325 }
326 $filename = 'filtered_memberslist.csv';
327 $filepath = CsvOut::DEFAULT_DIRECTORY . $filename;
328 $fp = fopen($filepath, 'w');
329 if ($fp) {
330 $res = $csv->export(
331 $members_list,
332 Csv::DEFAULT_SEPARATOR,
333 Csv::DEFAULT_QUOTE,
334 $labels,
335 $fp
336 );
337 fclose($fp);
338 $written[] = array(
339 'name' => $filename,
340 'file' => $filepath
341 );
342 }
343
344 $filepath = CsvOut::DEFAULT_DIRECTORY . $filename;
345 if (file_exists($filepath)) {
346 $response = $this->response->withHeader('Content-Description', 'File Transfer')
347 ->withHeader('Content-Type', 'text/csv')
348 ->withHeader('Content-Disposition', 'attachment;filename="' . $filename . '"')
349 ->withHeader('Pragma', 'no-cache')
350 ->withHeader('Content-Transfer-Encoding', 'binary')
351 ->withHeader('Expires', '0')
352 ->withHeader('Cache-Control', 'must-revalidate')
353 ->withHeader('Pragma', 'public')
354 ->withHeader('Content-Length', filesize($filepath));
355
356 $stream = fopen('php://memory', 'r+');
357 fwrite($stream, file_get_contents($filepath));
358 rewind($stream);
359
360 return $response->withBody(new \Slim\Http\Stream($stream));
361 } else {
362 Analog::log(
363 'A request has been made to get an exported file named `' .
364 $filename .'` that does not exists.',
365 Analog::WARNING
366 );
367 $notFound = $this->notFoundHandler;
368 return $notFound($request, $response);
369 }
370 return $response;
371 }
372 )->setName('csv-memberslist')->add($authenticate);
373
374 //members list
375 $app->get(
376 '/members[/{option:page|order}/{value:\d+}]',
377 function ($request, $response, $args = []) {
378 $option = null;
379 if (isset($args['option'])) {
380 $option = $args['option'];
381 }
382 $value = null;
383 if (isset($args['value'])) {
384 $value = $args['value'];
385 }
386
387 if (isset($this->session->filter_members)) {
388 $filters = $this->session->filter_members;
389 } else {
390 $filters = new MembersList();
391 }
392
393 if ($option !== null) {
394 switch ($option) {
395 case 'page':
396 $filters->current_page = (int)$value;
397 break;
398 case 'order':
399 $filters->orderby = $value;
400 break;
401 }
402 }
403
404 $members = new Members($filters);
405
406 $members_list = array();
407 if ($this->login->isAdmin() || $this->login->isStaff()) {
408 $members_list = $members->getMembersList(true);
409 } else {
410 $members_list = $members->getManagedMembersList(true);
411 }
412
413 $groups = new Groups($this->zdb, $this->login);
414 $groups_list = $groups->getList();
415
416 //assign pagination variables to the template and add pagination links
417 $filters->setSmartyPagination($this->router, $this->view->getSmarty(), false);
418 $filters->setViewCommonsFilters($this->preferences, $this->view->getSmarty());
419
420 $this->session->filter_members = $filters;
421
422 // display page
423 $this->view->render(
424 $response,
425 'gestion_adherents.tpl',
426 array(
427 'page_title' => _T("Members management"),
428 'require_dialog' => true,
429 'require_calendar' => true,
430 'require_mass' => true,
431 'members' => $members_list,
432 'filter_groups_options' => $groups_list,
433 'nb_members' => $members->getCount(),
434 'filters' => $filters,
435 'adv_filters' => $filters instanceof AdvancedMembersList
436 )
437 );
438 return $response;
439 }
440 )->setName(
441 'members'
442 )->add($authenticate);
443
444 //members list filtering
445 $app->post(
446 '/members/filter',
447 function ($request, $response) {
448 $post = $request->getParsedBody();
449 if (isset($this->session->filter_members)) {
450 //CAUTION: this one may be simple or advanced, display must change
451 $filters = $this->session->filter_members;
452 } else {
453 $filters = new MembersList();
454 }
455
456 //reintialize filters
457 if (isset($post['clear_filter'])) {
458 $filters = new MembersList();
459 } elseif (isset($post['clear_adv_filter'])) {
460 $this->session->filter_members = null;
461 unset($this->session->filter_members);
462
463 return $response
464 ->withStatus(301)
465 ->withHeader('Location', $this->router->pathFor('advanced-search'));
466 } elseif (isset($post['adv_criteria'])) {
467 return $response
468 ->withStatus(301)
469 ->withHeader('Location', $this->router->pathFor('advanced-search'));
470 } else {
471 //string to filter
472 if (isset($post['filter_str'])) { //filter search string
473 $filters->filter_str = stripslashes(
474 htmlspecialchars($post['filter_str'], ENT_QUOTES)
475 );
476 }
477 //field to filter
478 if (isset($post['field_filter'])) {
479 if (is_numeric($post['field_filter'])) {
480 $filters->field_filter = $post['field_filter'];
481 }
482 }
483 //membership to filter
484 if (isset($post['membership_filter'])) {
485 if (is_numeric($post['membership_filter'])) {
486 $filters->membership_filter
487 = $post['membership_filter'];
488 }
489 }
490 //account status to filter
491 if (isset($post['filter_account'])) {
492 if (is_numeric($post['filter_account'])) {
493 $filters->filter_account = $post['filter_account'];
494 }
495 }
496 //email filter
497 if (isset($post['email_filter'])) {
498 $filters->email_filter = (int)$post['email_filter'];
499 }
500 //group filter
501 if (isset($post['group_filter'])
502 && $post['group_filter'] > 0
503 ) {
504 $filters->group_filter = (int)$post['group_filter'];
505 }
506 //number of rows to show
507 if (isset($post['nbshow'])) {
508 $filters->show = $post['nbshow'];
509 }
510
511 if (isset($post['advanced_filtering'])) {
512 if (!$filters instanceof AdvancedMembersList) {
513 $filters = new AdvancedMembersList($filters);
514 }
515 //Advanced filters
516 $filters->reinit();
517 unset($post['advanced_filtering']);
518 $freed = false;
519 foreach ($post as $k => $v) {
520 if (strpos($k, 'free_', 0) === 0) {
521 if (!$freed) {
522 $i = 0;
523 foreach ($post['free_field'] as $f) {
524 if (trim($f) !== ''
525 && trim($post['free_text'][$i]) !== ''
526 ) {
527 $fs_search = $post['free_text'][$i];
528 $log_op
529 = (int)$post['free_logical_operator'][$i];
530 $qry_op
531 = (int)$post['free_query_operator'][$i];
532 $type = (int)$post['free_type'][$i];
533 $fs = array(
534 'idx' => $i,
535 'field' => $f,
536 'type' => $type,
537 'search' => $fs_search,
538 'log_op' => $log_op,
539 'qry_op' => $qry_op
540 );
541 $filters->free_search = $fs;
542 }
543 $i++;
544 }
545 $freed = true;
546 }
547 } else {
548 switch ($k) {
549 case 'contrib_min_amount':
550 case 'contrib_max_amount':
551 if (trim($v) !== '') {
552 $v = (float)$v;
553 } else {
554 $v = null;
555 }
556 break;
557 }
558 $filters->$k = $v;
559 }
560 }
561 }
562 }
563
564 if (isset($post['savesearch'])) {
565 return $response
566 ->withStatus(301)
567 ->withHeader(
568 'Location',
569 $this->router->pathFor(
570 'saveSearch',
571 $post
572 )
573 );
574 }
575
576 $this->session->filter_members = $filters;
577
578 return $response
579 ->withStatus(301)
580 ->withHeader('Location', $this->router->pathFor('members'));
581 }
582 )->setName('filter-memberslist')->add($authenticate);
583
584 //members self card
585 $app->get(
586 '/member/me',
587 function ($request, $response) {
588 if ($this->login->isSuperAdmin()) {
589 return $response
590 ->withStatus(301)
591 ->withHeader('Location', $this->router->pathFor('slash'));
592 }
593 $deps = array(
594 'picture' => true,
595 'groups' => true,
596 'dues' => true,
597 'parent' => true,
598 'children' => true,
599 'dynamics' => true
600 );
601
602 $member = new Adherent($this->zdb, $this->login->login, $deps);
603 $id = $member->id;
604
605 $fc = $this->fields_config;
606 $display_elements = $fc->getDisplayElements($this->login);
607
608 // display page
609 $this->view->render(
610 $response,
611 'voir_adherent.tpl',
612 array(
613 'page_title' => _T("Member Profile"),
614 'require_dialog' => true,
615 'member' => $member,
616 'pref_lang' => $this->i18n->getNameFromId($member->language),
617 'pref_card_self' => $this->preferences->pref_card_self,
618 'groups' => Groups::getSimpleList(),
619 'time' => time(),
620 'display_elements' => $display_elements
621 )
622 );
623 }
624 )->setName('me')->add($authenticate);
625
626 //members card
627 $app->get(
628 '/member/{id:\d+}',
629 function ($request, $response, $args) {
630 $id = $args['id'];
631
632 $deps = array(
633 'picture' => true,
634 'groups' => true,
635 'dues' => true,
636 'parent' => true,
637 'children' => true,
638 'dynamics' => true
639 );
640 $member = new Adherent($this->zdb, (int)$id, $deps);
641
642 if ($this->login->id != $id && !$this->login->isAdmin() && !$this->login->isStaff()) {
643 //check if requested member is part of managed groups
644 $groups = $member->groups;
645 $is_managed = false;
646 foreach ($groups as $g) {
647 if ($this->login->isGroupManager($g->getId())) {
648 $is_managed = true;
649 break;
650 }
651 }
652 if ($is_managed !== true) {
653 //requested member is not part of managed groups,
654 //fall back to logged in member
655 Analog::log(
656 'Trying to display member #' . $id . ' without appropriate ACLs',
657 Analog::WARNING
658 );
659
660 return $response
661 ->withStatus(403)
662 ->withHeader(
663 'Location',
664 $this->router->pathFor(
665 'member',
666 ['id' => $this->login->id]
667 )
668 );
669 }
670 }
671
672 if ($member->id == null) {
673 //member does not exists!
674 $this->flash->addMessage(
675 'error_detected',
676 str_replace('%id', $args['id'], _T("No member #%id."))
677 );
678
679 return $response
680 ->withStatus(404)
681 ->withHeader(
682 'Location',
683 $this->router->pathFor('slash')
684 );
685 }
686
687 // flagging fields visibility
688 $fc = $this->fields_config;
689 $display_elements = $fc->getDisplayElements($this->login);
690
691 // display page
692 $this->view->render(
693 $response,
694 'voir_adherent.tpl',
695 array(
696 'page_title' => _T("Member Profile"),
697 'require_dialog' => true,
698 'member' => $member,
699 'pref_lang' => $this->i18n->getNameFromId($member->language),
700 'pref_card_self' => $this->preferences->pref_card_self,
701 'groups' => Groups::getSimpleList(),
702 'time' => time(),
703 'display_elements' => $display_elements
704 )
705 );
706 return $response;
707 }
708 )->setName('member')->add($authenticate)->add($navMiddleware);
709
710 $app->get(
711 '/member/{action:edit|add}[/{id:\d+}]',
712 function ($request, $response, $args) {
713 $action = $args['action'];
714 $id = null;
715 if (isset($args['id'])) {
716 $id = $args['id'];
717 }
718
719 if ($action === 'edit' && $id === null) {
720 throw new \RuntimeException(
721 _T("Member ID cannot ben null calling edit route!")
722 );
723 } elseif ($action === 'add' && $id !== null) {
724 return $response
725 ->withStatus(301)
726 ->withHeader('Location', $this->router->pathFor('editmember', ['action' => 'add']));
727 }
728 $deps = array(
729 'picture' => true,
730 'groups' => true,
731 'dues' => true,
732 'parent' => true,
733 'children' => true,
734 'dynamics' => true
735 );
736 $route_params = [];
737
738 if ($this->session->member !== null) {
739 $member = $this->session->member;
740 $this->session->member = null;
741 } else {
742 $member = new Adherent($this->zdb, null, $deps);
743 }
744
745 if ($this->login->isAdmin() || $this->login->isStaff() || $this->login->isGroupManager()) {
746 if ($id !== null) {
747 if ($member->id != $id) {
748 $member->load($id);
749 }
750 if (!$this->login->isAdmin() && !$this->login->isStaff() && $this->login->isGroupManager()) {
751 //check if current logged in user can manage loaded member
752 $groups = $member->groups;
753 $can_manage = false;
754 foreach ($groups as $group) {
755 if ($this->login->isGroupManager($group->getId())) {
756 $can_manage = true;
757 break;
758 }
759 }
760 if ($can_manage !== true) {
761 Analog::log(
762 'Logged in member ' . $this->login->login .
763 ' has tried to load member #' . $member->id .
764 ' but do not manage any groups he belongs to.',
765 Analog::WARNING
766 );
767 $member->load($this->login->id);
768 }
769 }
770 }
771 } else {
772 if ($member->id != $id) {
773 $member->load($this->login->id);
774 }
775 }
776
777 // flagging required fields
778 $fc = $this->fields_config;
779
780 // password required if we create a new member
781 if ($member->id != '') {
782 $fc->setNotRequired('mdp_adh');
783 }
784
785 //handle requirements for parent fields
786 $parent_fields = $member->getParentFields();
787 foreach ($parent_fields as $key => $field) {
788 if ($fc->isRequired($field) && $member->hasParent()) {
789 $fc->setNotRequired($field);
790 } elseif (!$fc->isRequired($field)) {
791 unset($parent_fields[$key]);
792 }
793 }
794 $route_params['parent_fields'] = $parent_fields;
795
796 // flagging required fields invisible to members
797 if ($this->login->isAdmin() || $this->login->isStaff()) {
798 $fc->setNotRequired('activite_adh');
799 $fc->setNotRequired('id_statut');
800 }
801
802 // template variable declaration
803 $title = _T("Member Profile");
804 if ($member->id != '') {
805 $title .= ' (' . _T("modification") . ')';
806 } else {
807 $title .= ' (' . _T("creation") . ')';
808 }
809
810 //Status
811 $statuts = new Status($this->zdb);
812
813 //Groups
814 $groups = new Groups($this->zdb, $this->login);
815 $groups_list = $groups->getSimpleList(true);
816
817 $form_elements = $fc->getFormElements(
818 $this->login,
819 $member->id == ''
820 );
821
822 // members
823 $members = [];
824 $m = new Members();
825 $required_fields = array(
826 'id_adh',
827 'nom_adh',
828 'prenom_adh'
829 );
830 $list_members = $m->getList(false, $required_fields, true);
831
832 if (count($list_members) > 0) {
833 foreach ($list_members as $lmember) {
834 $pk = Adherent::PK;
835 $sname = mb_strtoupper($lmember->nom_adh, 'UTF-8') .
836 ' ' . ucwords(mb_strtolower($lmember->prenom_adh, 'UTF-8')) .
837 ' (' . $lmember->id_adh . ')';
838 $members[$lmember->$pk] = $sname;
839 }
840 }
841
842 $route_params['members'] = [
843 'filters' => $m->getFilters(),
844 'count' => $m->getCount()
845 ];
846
847 //check if current attached member is part of the list
848 if ($member->hasParent()) {
849 if (!isset($members[$member->parent->id])) {
850 $members =
851 [$member->parent->id => $member->parent->getSName()] +
852 $members
853 ;
854 }
855 }
856
857 if (count($members)) {
858 $route_params['members']['list'] = $members;
859 }
860
861 // display page
862 $this->view->render(
863 $response,
864 'member.tpl',
865 array_merge(
866 $route_params,
867 array(
868 'parent_tpl' => 'page.tpl',
869 'require_dialog' => true,
870 'autocomplete' => true,
871 'page_title' => $title,
872 'member' => $member,
873 'self_adh' => false,
874 'require_calendar' => true,
875 // pseudo random int
876 'time' => time(),
877 'titles_list' => Titles::getList($this->zdb),
878 'statuts' => $statuts->getList(),
879 'groups' => $groups_list,
880 'fieldsets' => $form_elements['fieldsets'],
881 'hidden_elements' => $form_elements['hiddens']
882 )
883 )
884 );
885 return $response;
886 }
887 )->setName(
888 'editmember'
889 )->add($authenticate)->add($navMiddleware);
890
891 $app->post(
892 '/member/store[/{self:subscribe}]',
893 function ($request, $response, $args) {
894 if (!$this->preferences->pref_bool_selfsubscribe && !$this->login->isLogged()) {
895 return $response
896 ->withStatus(301)
897 ->withHeader('Location', $this->router->pathFor('slash'));
898 }
899
900 $post = $request->getParsedBody();
901 $deps = array(
902 'picture' => true,
903 'groups' => true,
904 'dues' => true,
905 'parent' => true,
906 'children' => true,
907 'dynamics' => true
908 );
909 $member = new Adherent($this->zdb, null, $deps);
910 $member->setDependencies(
911 $this->preferences,
912 $this->members_fields,
913 $this->history
914 );
915 if (isset($args['self'])) {
916 //mark as self membership
917 $member->setSelfMembership();
918 }
919
920 $success_detected = [];
921 $warning_detected = [];
922 $error_detected = [];
923
924 // new or edit
925 $adherent['id_adh'] = get_numeric_form_value('id_adh', '');
926
927 if ($this->login->isAdmin() || $this->login->isStaff() || $this->login->isGroupManager()) {
928 if ($adherent['id_adh']) {
929 $member->load($adherent['id_adh']);
930 if (!$this->login->isAdmin() && !$this->login->isStaff() && $this->login->isGroupManager()) {
931 //check if current logged in user can manage loaded member
932 $groups = $member->groups;
933 $can_manage = false;
934 foreach ($groups as $group) {
935 if ($this->login->isGroupManager($group->getId())) {
936 $can_manage = true;
937 break;
938 }
939 }
940 if ($can_manage !== true) {
941 Analog::log(
942 'Logged in member ' . $this->login->login .
943 ' has tried to load member #' . $member->id .
944 ' but do not manage any groups he belongs to.',
945 Analog::WARNING
946 );
947 $member->load($this->login->id);
948 }
949 }
950 }
951 } else {
952 $member->load($this->login->id);
953 $adherent['id_adh'] = $this->login->id;
954 }
955
956 // flagging required fields
957 $fc = $this->fields_config;
958
959 // password required if we create a new member
960 if ($member->id != '') {
961 $fc->setNotRequired('mdp_adh');
962 }
963
964 if ($member->hasParent() && !isset($post['detach_parent'])
965 || isset($post['parent_id']) && !empty($post['parent_id'])
966 ) {
967 $parent_fields = $member->getParentFields();
968 foreach ($parent_fields as $field) {
969 if ($fc->isRequired($field)) {
970 $fc->setNotRequired($field);
971 }
972 }
973 }
974
975 // flagging required fields invisible to members
976 if ($this->login->isAdmin() || $this->login->isStaff()) {
977 $fc->setNotRequired('activite_adh');
978 $fc->setNotRequired('id_statut');
979 }
980
981 $form_elements = $fc->getFormElements(
982 $this->login,
983 $member->id == '',
984 isset($args['self'])
985 );
986 $fieldsets = $form_elements['fieldsets'];
987 $required = array();
988 $disabled = array();
989
990 foreach ($fieldsets as $category) {
991 foreach ($category->elements as $field) {
992 if ($field->required == true) {
993 $required[$field->field_id] = true;
994 }
995 if ($field->disabled == true) {
996 $disabled[$field->field_id] = true;
997 } elseif (!isset($post[$field->field_id])) {
998 switch ($field->field_id) {
999 //unchecked booleans are not sent from form
1000 case 'bool_admin_adh':
1001 case 'bool_exempt_adh':
1002 case 'bool_display_info':
1003 $post[$field->field_id] = 0;
1004 break;
1005 }
1006 }
1007 }
1008 }
1009
1010 $real_requireds = array_diff(array_keys($required), array_keys($disabled));
1011
1012 // Validation
1013 if (isset($post[array_shift($real_requireds)])) {
1014 // regular fields
1015 $valid = $member->check($post, $required, $disabled);
1016 if ($valid !== true) {
1017 $error_detected = array_merge($error_detected, $valid);
1018 }
1019
1020 if (count($error_detected) == 0) {
1021 //all goes well, we can proceed
1022
1023 $new = false;
1024 if ($member->id == '') {
1025 $new = true;
1026 }
1027 $store = $member->store();
1028 if ($store === true) {
1029 //member has been stored :)
1030 if ($new) {
1031 if (isset($args['self'])) {
1032 $success_detected[] = _T("Your account has been created!");
1033 if ($this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED
1034 && $member->getEmail() != ''
1035 ) {
1036 $success_detected[] = _T("An email has been sent to you, check your inbox.");
1037 }
1038 } else {
1039 $success_detected[] = _T("New member has been successfully added.");
1040 }
1041 //Send email to admin if preference checked
1042 if ($this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED
1043 && $this->preferences->pref_bool_mailadh
1044 ) {
1045 $texts = new Texts(
1046 $this->texts_fields,
1047 $this->preferences,
1048 $this->router,
1049 array(
1050 'name_adh' => custom_html_entity_decode(
1051 $member->sname
1052 ),
1053 'firstname_adh' => custom_html_entity_decode(
1054 $member->surname
1055 ),
1056 'lastname_adh' => custom_html_entity_decode(
1057 $member->name
1058 ),
1059 'mail_adh' => custom_html_entity_decode(
1060 $member->email
1061 ),
1062 'login_adh' => custom_html_entity_decode(
1063 $member->login
1064 )
1065 )
1066 );
1067 $mtxt = $texts->getTexts(
1068 (isset($args['self']) ? 'newselfadh' : 'newadh'),
1069 $this->preferences->pref_lang
1070 );
1071
1072 $mail = new GaletteMail($this->preferences);
1073 $mail->setSubject($texts->getSubject());
1074 $recipients = [];
1075 foreach ($this->preferences->vpref_email as $pref_email) {
1076 $recipients[$pref_email] = $this->preferences->pref_email_nom;
1077 }
1078 $mail->setRecipients($recipients);
1079 $mail->setMessage($texts->getBody());
1080 $sent = $mail->send();
1081
1082 if ($sent == GaletteMail::MAIL_SENT) {
1083 $this->history->add(
1084 str_replace(
1085 '%s',
1086 $member->sname . ' (' . $member->email . ')',
1087 _T("New account mail sent to admin for '%s'.")
1088 )
1089 );
1090 } else {
1091 $str = str_replace(
1092 '%s',
1093 $member->sname . ' (' . $member->email . ')',
1094 _T("A problem happened while sending email to admin for account '%s'.")
1095 );
1096 $this->history->add($str);
1097 $warning_detected[] = $str;
1098 }
1099 unset($texts);
1100 }
1101 } else {
1102 $success_detected[] = _T("Member account has been modified.");
1103 }
1104
1105 // send mail to member
1106 if (isset($args['self']) || isset($post['mail_confirm']) && $post['mail_confirm'] == '1') {
1107 if ($this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED) {
1108 if ($member->getEmail() == '' && !isset($args['self'])) {
1109 $error_detected[] = _T("- You can't send a confirmation by email if the member hasn't got an address!");
1110 } else {
1111 $mreplaces = [
1112 'name_adh' => custom_html_entity_decode(
1113 $member->sname
1114 ),
1115 'firstname_adh' => custom_html_entity_decode(
1116 $member->surname
1117 ),
1118 'lastname_adh' => custom_html_entity_decode(
1119 $member->name
1120 ),
1121 'mail_adh' => custom_html_entity_decode(
1122 $member->getEmail()
1123 ),
1124 'login_adh' => custom_html_entity_decode(
1125 $member->login
1126 )
1127 ];
1128 if ($new) {
1129 $password = new Password($this->zdb);
1130 $res = $password->generateNewPassword($member->id);
1131 if ($res == true) {
1132 $link_validity = new DateTime();
1133 $link_validity->add(new DateInterval('PT24H'));
1134 $mreplaces['change_pass_uri'] = $this->preferences->getURL() .
1135 $this->router->pathFor(
1136 'password-recovery',
1137 ['hash' => base64_encode($password->getHash())]
1138 );
1139 $mreplaces['link_validity'] = $link_validity->format(_T("Y-m-d H:i:s"));
1140 } else {
1141 $str = str_replace(
1142 '%s',
1143 $login_adh,
1144 _T("An error occurred storing temporary password for %s. Please inform an admin.")
1145 );
1146 $this->history->add($str);
1147 $this->flash->addMessage(
1148 'error_detected',
1149 $str
1150 );
1151 }
1152 }
1153
1154 //send mail to member
1155 // Get email text in database
1156 $texts = new Texts(
1157 $this->texts_fields,
1158 $this->preferences,
1159 $this->router,
1160 $mreplaces
1161 );
1162 $mlang = $this->preferences->pref_lang;
1163 if (isset($post['pref_lang'])) {
1164 $mlang = $post['pref_lang'];
1165 }
1166 $mtxt = $texts->getTexts(
1167 (($new) ? 'sub' : 'accountedited'),
1168 $mlang
1169 );
1170
1171 $mail = new GaletteMail($this->preferences);
1172 $mail->setSubject($texts->getSubject());
1173 $mail->setRecipients(
1174 array(
1175 $member->getEmail() => $member->sname
1176 )
1177 );
1178 $mail->setMessage($texts->getBody());
1179 $sent = $mail->send();
1180
1181 if ($sent == GaletteMail::MAIL_SENT) {
1182 $msg = str_replace(
1183 '%s',
1184 $member->sname . ' (' . $member->getEmail() . ')',
1185 ($new) ?
1186 _T("New account mail sent to '%s'.") :
1187 _T("Account modification mail sent to '%s'.")
1188 );
1189 $this->history->add($msg);
1190 $success_detected[] = $msg;
1191 } else {
1192 $str = str_replace(
1193 '%s',
1194 $member->sname . ' (' . $member->getEmail() . ')',
1195 _T("A problem happened while sending account mail to '%s'")
1196 );
1197 $this->history->add($str);
1198 $error_detected[] = $str;
1199 }
1200 }
1201 } elseif ($this->preferences->pref_mail_method == GaletteMail::METHOD_DISABLED) {
1202 //if mail has been disabled in the preferences, we should not be here ;
1203 //we do not throw an error, just a simple warning that will be show later
1204 $msg = _T("You asked Galette to send a confirmation mail to the member, but mail has been disabled in the preferences.");
1205 $warning_detected[] = $msg;
1206 }
1207 }
1208
1209 // send mail to admin
1210 if ($this->preferences->pref_mail_method > GaletteMail::METHOD_DISABLED
1211 && $this->preferences->pref_bool_mailadh
1212 && !$new
1213 && $member->id == $this->login->id
1214 ) {
1215 $mreplaces = [
1216 'name_adh' => custom_html_entity_decode(
1217 $member->sname
1218 ),
1219 'firstname_adh' => custom_html_entity_decode(
1220 $member->surname
1221 ),
1222 'lastname_adh' => custom_html_entity_decode(
1223 $member->name
1224 ),
1225 'mail_adh' => custom_html_entity_decode(
1226 $member->getEmail()
1227 ),
1228 'login_adh' => custom_html_entity_decode(
1229 $member->login
1230 )
1231 ];
1232
1233 //send mail to member
1234 // Get email text in database
1235 $texts = new Texts(
1236 $this->texts_fields,
1237 $this->preferences,
1238 $this->router,
1239 $mreplaces
1240 );
1241 $mlang = $this->preferences->pref_lang;
1242
1243 $mtxt = $texts->getTexts(
1244 'admaccountedited',
1245 $mlang
1246 );
1247
1248 $mail = new GaletteMail($this->preferences);
1249 $mail->setSubject($texts->getSubject());
1250 $recipients = [];
1251 foreach ($this->preferences->vpref_email_newadh as $pref_email) {
1252 $recipients[$pref_email] = $this->preferences->pref_email_nom;
1253 }
1254 $mail->setRecipients($recipients);
1255
1256 $mail->setMessage($texts->getBody());
1257 $sent = $mail->send();
1258
1259 if ($sent == GaletteMail::MAIL_SENT) {
1260 $msg = _T("Account modification mail sent to admin.");
1261 $this->history->add($msg);
1262 $success_detected[] = $msg;
1263 } else {
1264 $str = _T("A problem happened while sending account mail to admin");
1265 $this->history->add($str);
1266 $error_detected[] = $str;
1267 }
1268 }
1269
1270 //store requested groups
1271 $add_groups = null;
1272 $groups_adh = null;
1273 $managed_groups_adh = null;
1274
1275 //add/remove user from groups
1276 if (isset($post['groups_adh'])) {
1277 $groups_adh = $post['groups_adh'];
1278 }
1279 $add_groups = Groups::addMemberToGroups(
1280 $member,
1281 $groups_adh
1282 );
1283
1284 if ($add_groups === false) {
1285 $error_detected[] = _T("An error occurred adding member to its groups.");
1286 }
1287
1288 //add/remove manager from groups
1289 if (isset($post['groups_managed_adh'])) {
1290 $managed_groups_adh = $post['groups_managed_adh'];
1291 }
1292 $add_groups = Groups::addMemberToGroups(
1293 $member,
1294 $managed_groups_adh,
1295 true
1296 );
1297 $member->loadGroups();
1298
1299 if ($add_groups === false) {
1300 $error_detected[] = _T("An error occurred adding member to its groups as manager.");
1301 }
1302 } else {
1303 //something went wrong :'(
1304 $error_detected[] = _T("An error occurred while storing the member.");
1305 }
1306 }
1307
1308 if (count($error_detected) == 0) {
1309 $files_res = $member->handleFiles($_FILES);
1310 if (is_array($files_res)) {
1311 $error_detected = array_merge($error_detected, $files_res);
1312 }
1313
1314 if (isset($post['del_photo'])) {
1315 if (!$member->picture->delete($member->id)) {
1316 $error_detected[] = _T("Delete failed");
1317 $str_adh = $member->id . ' (' . $member->sname . ' ' . ')';
1318 Analog::log(
1319 'Unable to delete picture for member ' . $str_adh,
1320 Analog::ERROR
1321 );
1322 }
1323 }
1324 }
1325
1326 if (count($error_detected) > 0) {
1327 foreach ($error_detected as $error) {
1328 if (strpos($error, '%member_url_') !== false) {
1329 preg_match('/%member_url_(\d+)/', $error, $matches);
1330 $url = $this->router->pathFor('member', ['id' => $matches[1]]);
1331 $error = str_replace(
1332 '%member_url_' . $matches[1],
1333 $url,
1334 $error
1335 );
1336 }
1337 $this->flash->addMessage(
1338 'error_detected',
1339 $error
1340 );
1341 }
1342 }
1343
1344 if (count($warning_detected) > 0) {
1345 foreach ($warning_detected as $warning) {
1346 $this->flash->addMessage(
1347 'warning_detected',
1348 $warning
1349 );
1350 }
1351 }
1352 if (count($success_detected) > 0) {
1353 foreach ($success_detected as $success) {
1354 $this->flash->addMessage(
1355 'success_detected',
1356 $success
1357 );
1358 }
1359 }
1360
1361 if (count($error_detected) == 0) {
1362 $redirect_url = null;
1363 if (isset($args['self'])) {
1364 $redirect_url = $this->router->pathFor('login');
1365 } elseif (isset($post['redirect_on_create'])
1366 && $post['redirect_on_create'] > Adherent::AFTER_ADD_DEFAULT
1367 ) {
1368 switch ($post['redirect_on_create']) {
1369 case Adherent::AFTER_ADD_TRANS:
1370 $redirect_url = $this->router->pathFor('transaction', ['action' => 'add']);
1371 break;
1372 case Adherent::AFTER_ADD_NEW:
1373 $redirect_url = $this->router->pathFor('editmember', ['action' => 'add']);
1374 break;
1375 case Adherent::AFTER_ADD_SHOW:
1376 $redirect_url = $this->router->pathFor('member', ['id' => $member->id]);
1377 break;
1378 case Adherent::AFTER_ADD_LIST:
1379 $redirect_url = $this->router->pathFor('members');
1380 break;
1381 case Adherent::AFTER_ADD_HOME:
1382 $redirect_url = $this->router->pathFor('slash');
1383 break;
1384 }
1385 } elseif (!isset($post['id_adh']) && !$member->isDueFree()) {
1386 $redirect_url = $this->router->pathFor(
1387 'contribution',
1388 [
1389 'type' => 'fee',
1390 'action' => 'add',
1391 ]
1392 ) . '?id_adh=' . $member->id;
1393 } else {
1394 $redirect_url = $this->router->pathFor('member', ['id' => $member->id]);
1395 }
1396
1397 return $response
1398 ->withStatus(301)
1399 ->withHeader('Location', $redirect_url);
1400 } else {
1401 //store entity in session
1402 $this->session->member = $member;
1403
1404 if (isset($args['self'])) {
1405 $redirect_url = $this->router->pathFor('subscribe');
1406 } else {
1407 if ($member->id) {
1408 $rparams = [
1409 'id' => $member->id,
1410 'action' => 'edit'
1411 ];
1412 } else {
1413 $rparams = ['action' => 'add'];
1414 }
1415 $redirect_url = $this->router->pathFor(
1416 'editmember',
1417 $rparams
1418 );
1419 }
1420
1421 return $response
1422 ->withStatus(301)
1423 ->withHeader('Location', $redirect_url);
1424 }
1425 }
1426 }
1427 )->setName('storemembers');
1428
1429 $app->get(
1430 '/member/remove/{id:\d+}',
1431 function ($request, $response, $args) {
1432 $adh = new Adherent($this->zdb, (int)$args['id']);
1433
1434 $data = [
1435 'id' => $args['id'],
1436 'redirect_uri' => $this->router->pathFor('members')
1437 ];
1438
1439 // display page
1440 $this->view->render(
1441 $response,
1442 'confirm_removal.tpl',
1443 array(
1444 'type' => _T("Member"),
1445 'mode' => $request->isXhr() ? 'ajax' : '',
1446 'page_title' => sprintf(
1447 _T('Remove member %1$s'),
1448 $adh->sfullname
1449 ),
1450 'form_url' => $this->router->pathFor('doRemoveMember', ['id' => $adh->id]),
1451 'cancel_uri' => $this->router->pathFor('members'),
1452 'data' => $data
1453 )
1454 );
1455 return $response;
1456 }
1457 )->setName('removeMember')->add($authenticate);
1458
1459 $app->get(
1460 '/members/remove',
1461 function ($request, $response) {
1462 $filters = $this->session->filter_members;
1463
1464 $data = [
1465 'id' => $filters->selected,
1466 'redirect_uri' => $this->router->pathFor('members')
1467 ];
1468
1469 // display page
1470 $this->view->render(
1471 $response,
1472 'confirm_removal.tpl',
1473 array(
1474 'type' => _T("Member"),
1475 'mode' => $request->isXhr() ? 'ajax' : '',
1476 'page_title' => _T('Remove members'),
1477 'message' => str_replace(
1478 '%count',
1479 count($data['id']),
1480 _T('You are about to remove %count members.')
1481 ),
1482 'form_url' => $this->router->pathFor('doRemoveMember'),
1483 'cancel_uri' => $this->router->pathFor('members'),
1484 'data' => $data
1485 )
1486 );
1487 return $response;
1488 }
1489 )->setName('removeMembers')->add($authenticate);
1490
1491 $app->post(
1492 '/member/remove' . '[/{id:\d+}]',
1493 function ($request, $response) {
1494 $post = $request->getParsedBody();
1495 $ajax = isset($post['ajax']) && $post['ajax'] === 'true';
1496 $success = false;
1497
1498 $uri = isset($post['redirect_uri']) ?
1499 $post['redirect_uri'] :
1500 $this->router->pathFor('slash');
1501
1502 if (!isset($post['confirm'])) {
1503 $this->flash->addMessage(
1504 'error_detected',
1505 _T("Removal has not been confirmed!")
1506 );
1507 } else {
1508 if (isset($this->session->filter_members)) {
1509 $filters = $this->session->filter_members;
1510 } else {
1511 $filters = new MembersList();
1512 }
1513 $members = new Members($filters);
1514
1515 if (!is_array($post['id'])) {
1516 //delete member
1517 $adh = new Adherent($this->zdb, (int)$post['id']);
1518 $ids = (array)$post['id'];
1519 } else {
1520 $ids = $post['id'];
1521 }
1522
1523 $del = $members->removeMembers($ids);
1524
1525 if ($del !== true) {
1526 if (count($ids) === 1) {
1527 $error_detected = str_replace(
1528 '%name',
1529 $adh->sname,
1530 _T("An error occurred trying to remove member %name :/")
1531 );
1532 } else {
1533 $error_detected = _T("An error occurred trying to remove members :/");
1534 }
1535
1536 $this->flash->addMessage(
1537 'error_detected',
1538 $error_detected
1539 );
1540 } else {
1541 if (!is_array($post['id'])) {
1542 $success_detected = str_replace(
1543 '%name',
1544 $adh->sname,
1545 _T("Member %name has been successfully deleted.")
1546 );
1547 } else {
1548 $success_detected = str_replace(
1549 '%count',
1550 count($ids),
1551 _T("%count members have been successfully deleted.")
1552 );
1553 }
1554
1555 $this->flash->addMessage(
1556 'success_detected',
1557 $success_detected
1558 );
1559
1560 $success = true;
1561 }
1562 }
1563
1564 if (!$ajax) {
1565 return $response
1566 ->withStatus(301)
1567 ->withHeader('Location', $uri);
1568 } else {
1569 return $response->withJson(
1570 [
1571 'success' => $success
1572 ]
1573 );
1574 }
1575 }
1576 )->setName('doRemoveMember')->add($authenticate);
1577
1578 //advanced search page
1579 $app->get(
1580 '/advanced-search',
1581 function ($request, $response) {
1582 if (isset($this->session->filter_members)) {
1583 $filters = $this->session->filter_members;
1584 if (!$filters instanceof AdvancedMembersList) {
1585 $filters = new AdvancedMembersList($filters);
1586 }
1587 } else {
1588 $filters = new AdvancedMembersList();
1589 }
1590
1591 $groups = new Groups($this->zdb, $this->login);
1592 $groups_list = $groups->getList();
1593
1594 //we want only visibles fields
1595 $fields = $this->members_fields;
1596 $fc = $this->fields_config;
1597 $visibles = $fc->getVisibilities();
1598 $access_level = $this->login->getAccessLevel();
1599
1600 //remove not searchable fields
1601 unset($fields['mdp_adh']);
1602
1603 foreach ($fields as $k => $f) {
1604 if ($visibles[$k] == FieldsConfig::NOBODY ||
1605 ($visibles[$k] == FieldsConfig::ADMIN &&
1606 $access_level < Authentication::ACCESS_ADMIN) ||
1607 ($visibles[$k] == FieldsConfig::STAFF &&
1608 $access_level < Authentication::ACCESS_STAFF) ||
1609 ($visibles[$k] == FieldsConfig::MANAGER &&
1610 $access_level < Authentication::ACCESS_MANAGER)
1611 ) {
1612 unset($fields[$k]);
1613 }
1614 }
1615
1616 //add status label search
1617 if ($pos = array_search(Status::PK, array_keys($fields))) {
1618 $fields = array_slice($fields, 0, $pos, true) +
1619 ['status_label' => ['label' => _T('Status label')]] +
1620 array_slice($fields, $pos, count($fields) -1, true);
1621 }
1622
1623 //dynamic fields
1624 $deps = array(
1625 'picture' => false,
1626 'groups' => false,
1627 'dues' => false,
1628 'parent' => false,
1629 'children' => false,
1630 'dynamics' => false
1631 );
1632 $member = new Adherent($this->zdb, $this->login->login, $deps);
1633 $adh_dynamics = new DynamicFieldsHandle($this->zdb, $this->login, $member);
1634
1635 $contrib = new Contribution($this->zdb, $this->login);
1636 $contrib_dynamics = new DynamicFieldsHandle($this->zdb, $this->login, $contrib);
1637
1638 //Status
1639 $statuts = new Status($this->zdb);
1640
1641 //Contributions types
1642 $ct = new Galette\Entity\ContributionsTypes($this->zdb);
1643
1644 //Payments types
1645 $ptypes = new PaymentTypes(
1646 $this->zdb,
1647 $this->preferences,
1648 $this->login
1649 );
1650 $ptlist = $ptypes->getList();
1651
1652 $filters->setViewCommonsFilters($this->preferences, $this->view->getSmarty());
1653
1654 // display page
1655 $this->view->render(
1656 $response,
1657 'advanced_search.tpl',
1658 array(
1659 'page_title' => _T("Advanced search"),
1660 'require_dialog' => true,
1661 'require_calendar' => true,
1662 'require_sorter' => true,
1663 'filter_groups_options' => $groups_list,
1664 'search_fields' => $fields,
1665 'adh_dynamics' => $adh_dynamics->getFields(),
1666 'contrib_dynamics' => $contrib_dynamics->getFields(),
1667 'statuts' => $statuts->getList(),
1668 'contributions_types' => $ct->getList(),
1669 'filters' => $filters,
1670 'payments_types' => $ptlist
1671 )
1672 );
1673 return $response;
1674 }
1675 )->setName('advanced-search')->add($authenticate);
1676
1677 //Batch actions on members list
1678 $app->post(
1679 '/members/batch',
1680 function ($request, $response) {
1681 $post = $request->getParsedBody();
1682
1683 if (isset($post['member_sel'])) {
1684 if (isset($this->session->filter_members)) {
1685 $filters = $this->session->filter_members;
1686 } else {
1687 $filters = new MembersList();
1688 }
1689
1690 $filters->selected = $post['member_sel'];
1691 $this->session->filter_members = $filters;
1692
1693 if (isset($post['cards'])) {
1694 return $response
1695 ->withStatus(301)
1696 ->withHeader('Location', $this->router->pathFor('pdf-members-cards'));
1697 }
1698
1699 if (isset($post['labels'])) {
1700 return $response
1701 ->withStatus(301)
1702 ->withHeader('Location', $this->router->pathFor('pdf-members-labels'));
1703 }
1704
1705 if (isset($post['mailing'])) {
1706 return $response
1707 ->withStatus(301)
1708 ->withHeader('Location', $this->router->pathFor('mailing') . '?new=new');
1709 }
1710
1711 if (isset($post['attendance_sheet'])) {
1712 return $response
1713 ->withStatus(301)
1714 ->withHeader('Location', $this->router->pathFor('attendance_sheet_details'));
1715 }
1716
1717 if (isset($post['csv'])) {
1718 return $response
1719 ->withStatus(301)
1720 ->withHeader('Location', $this->router->pathFor('csv-memberslist'));
1721 }
1722
1723 if (isset($post['delete'])) {
1724 return $response
1725 ->withStatus(301)
1726 ->withHeader('Location', $this->router->pathFor('removeMembers'));
1727 }
1728
1729 if (isset($post['masschange'])) {
1730 return $response
1731 ->withStatus(301)
1732 ->withHeader('Location', $this->router->pathFor('masschangeMembers'));
1733 }
1734
1735 throw new \RuntimeException('Does not know what to batch :(');
1736 } else {
1737 $this->flash->addMessage(
1738 'error_detected',
1739 _T("No member was selected, please check at least one name.")
1740 );
1741
1742 return $response
1743 ->withStatus(301)
1744 ->withHeader('Location', $this->router->pathFor('members'));
1745 }
1746 }
1747 )->setName('batch-memberslist')->add($authenticate);
1748
1749 //PDF members cards
1750 $app->get(
1751 '/members/cards[/{' . Adherent::PK . ':\d+}]',
1752 function ($request, $response, $args) {
1753 if ($this->session->filter_members) {
1754 $filters = $this->session->filter_members;
1755 } else {
1756 $filters = new MembersList();
1757 }
1758
1759 if (isset($args[Adherent::PK])
1760 && $args[Adherent::PK] > 0
1761 ) {
1762 $id_adh = $args[Adherent::PK];
1763 $denied = false;
1764 if ($this->login->id != $id_adh
1765 && !$this->login->isAdmin()
1766 && !$this->login->isStaff()
1767 && !$this->login->isGroupManager()
1768 ) {
1769 $denied = true;
1770 }
1771
1772 if (!$this->login->isAdmin() && !$this->login->isStaff() && $this->login->id != $id_adh) {
1773 if ($this->login->isGroupManager()) {
1774 $adh = new Adherent($this->zdb, $id_adh, ['dynamics' => true]);
1775 //check if current logged in user can manage loaded member
1776 $groups = $adh->groups;
1777 $can_manage = false;
1778 foreach ($groups as $group) {
1779 if ($this->login->isGroupManager($group->getId())) {
1780 $can_manage = true;
1781 break;
1782 }
1783 }
1784 if ($can_manage !== true) {
1785 Analog::log(
1786 'Logged in member ' . $this->login->login .
1787 ' has tried to load member #' . $adh->id .
1788 ' but do not manage any groups he belongs to.',
1789 Analog::WARNING
1790 );
1791 $denied = true;
1792 }
1793 } else {
1794 $denied = true;
1795 }
1796 }
1797
1798 if ($denied) {
1799 //requested member cannot be managed. Load logged in user
1800 $id_adh = (int)$this->login->id;
1801 }
1802
1803 //check if member is up to date
1804 if ($this->login->id == $id_adh) {
1805 $adh = new Adherent($this->zdb, (int)$id_adh, ['dues' => true]);
1806 if (!$adh->isUp2Date()) {
1807 Analog::log(
1808 'Member ' . $id_adh . ' is not up to date; cannot get his PDF member card',
1809 Analog::WARNING
1810 );
1811 return $response
1812 ->withStatus(301)
1813 ->withHeader('Location', $this->router->pathFor('slash'));
1814 }
1815 }
1816
1817 // If we are called from a member's card, get unique id value
1818 $unique = $id_adh;
1819 } else {
1820 if (count($filters->selected) == 0) {
1821 Analog::log(
1822 'No member selected to generate members cards',
1823 Analog::INFO
1824 );
1825 $this->flash->addMessage(
1826 'error_detected',
1827 _T("No member was selected, please check at least one name.")
1828 );
1829
1830 return $response
1831 ->withStatus(301)
1832 ->withHeader('Location', $this->router->pathFor('members'));
1833 }
1834 }
1835
1836 // Fill array $selected with selected ids
1837 $selected = array();
1838 if (isset($unique) && $unique) {
1839 $selected[] = $unique;
1840 } else {
1841 $selected = $filters->selected;
1842 }
1843
1844 $m = new Members();
1845 $members = $m->getArrayList(
1846 $selected,
1847 array('nom_adh', 'prenom_adh'),
1848 true
1849 );
1850
1851 if (!is_array($members) || count($members) < 1) {
1852 Analog::log(
1853 'An error has occurred, unable to get members list.',
1854 Analog::ERROR
1855 );
1856
1857 $this->flash->addMessage(
1858 'error_detected',
1859 _T("Unable to get members list.")
1860 );
1861
1862 return $response
1863 ->withStatus(301)
1864 ->withHeader('Location', $this->router->pathFor('members'));
1865 }
1866
1867 $pdf = new PdfMembersCards($this->preferences);
1868 $pdf->drawCards($members);
1869
1870 $response = $this->response->withHeader('Content-type', 'application/pdf')
1871 ->withHeader('Content-Disposition', 'attachment;filename="' . $pdf->getFileName() . '"');
1872 $response->write($pdf->download());
1873 return $response;
1874 }
1875 )->setName('pdf-members-cards')->add($authenticate);
1876
1877 //PDF members labels
1878 $app->get(
1879 '/members/labels',
1880 function ($request, $response) {
1881 $get = $request->getQueryParams();
1882
1883 if ($this->session->filter_reminders_labels) {
1884 $filters = $this->session->filter_reminders_labels;
1885 unset($this->session->filter_reminders_labels);
1886 } elseif ($this->session->filter_members) {
1887 $filters = $this->session->filter_members;
1888 } else {
1889 $filters = new MembersList();
1890 }
1891
1892 $members = null;
1893 if (isset($get['from'])
1894 && $get['from'] === 'mailing'
1895 ) {
1896 //if we're from mailing, we have to retrieve
1897 //its unreachables members for labels
1898 $mailing = $this->session->mailing;
1899 $members = $mailing->unreachables;
1900 } else {
1901 if (count($filters->selected) == 0) {
1902 Analog::log('No member selected to generate labels', Analog::INFO);
1903 $this->flash->addMessage(
1904 'error_detected',
1905 _T("No member was selected, please check at least one name.")
1906 );
1907
1908 return $response
1909 ->withStatus(301)
1910 ->withHeader('Location', $this->router->pathFor('members'));
1911 }
1912
1913 $m = new Members();
1914 $members = $m->getArrayList(
1915 $filters->selected,
1916 array('nom_adh', 'prenom_adh')
1917 );
1918 }
1919
1920 if (!is_array($members) || count($members) < 1) {
1921 Analog::log(
1922 'An error has occurred, unable to get members list.',
1923 Analog::ERROR
1924 );
1925
1926 $this->flash->addMessage(
1927 'error_detected',
1928 _T("Unable to get members list.")
1929 );
1930
1931 return $response
1932 ->withStatus(301)
1933 ->withHeader('Location', $this->router->pathFor('members'));
1934 }
1935
1936 $pdf = new PdfMembersLabels($this->preferences);
1937 $pdf->drawLabels($members);
1938 $response = $this->response->withHeader('Content-type', 'application/pdf')
1939 ->withHeader('Content-Disposition', 'attachment;filename="' . $pdf->getFileName() . '"');
1940 $response->write($pdf->download());
1941 return $response;
1942 }
1943 )->setName('pdf-members-labels')->add($authenticate);
1944
1945 //PDF adhesion form
1946 $app->get(
1947 '/members/adhesion-form/{' . Adherent::PK . ':\d+}',
1948 function ($request, $response, $args) {
1949 $id_adh = (int)$args[Adherent::PK];
1950
1951 $denied = false;
1952 if ($this->login->id != $args['id']
1953 && !$this->login->isAdmin()
1954 && !$this->login->isStaff()
1955 && !$this->login->isGroupManager()
1956 ) {
1957 $denied = true;
1958 }
1959
1960 if (!$this->login->isAdmin() && !$this->login->isStaff() && $this->login->id != $args['id']) {
1961 if ($this->login->isGroupManager()) {
1962 $adh = new Adherent($this->zdb, $id_adh, ['dynamics' => true]);
1963 //check if current logged in user can manage loaded member
1964 $groups = $adh->groups;
1965 $can_manage = false;
1966 foreach ($groups as $group) {
1967 if ($this->login->isGroupManager($group->getId())) {
1968 $can_manage = true;
1969 break;
1970 }
1971 }
1972 if ($can_manage !== true) {
1973 Analog::log(
1974 'Logged in member ' . $this->login->login .
1975 ' has tried to load member #' . $adh->id .
1976 ' but do not manage any groups he belongs to.',
1977 Analog::WARNING
1978 );
1979 $denied = true;
1980 }
1981 } else {
1982 $denied = true;
1983 }
1984 }
1985
1986 if ($denied) {
1987 //requested member cannot be managed. Load logged in user
1988 $id_adh = (int)$this->login->id;
1989 }
1990 $adh = new Adherent($this->zdb, $id_adh, ['dynamics' => true]);
1991
1992 $form = $this->preferences->pref_adhesion_form;
1993 $pdf = new $form($adh, $this->zdb, $this->preferences);
1994 $response = $this->response->withHeader('Content-type', 'application/pdf')
1995 ->withHeader('Content-Disposition', 'attachment; filename="' . $pdf->getFileName() . '"');
1996 $response->write($pdf->download());
1997 return $response;
1998 }
1999 )->setName('adhesionForm')->add($authenticate);
2000
2001 //Empty PDF adhesion form
2002 $app->get(
2003 '/members/empty-adhesion-form',
2004 function ($request, $response) {
2005 $form = $this->preferences->pref_adhesion_form;
2006 $pdf = new $form(null, $this->zdb, $this->preferences);
2007 $response = $this->response->withHeader('Content-type', 'application/pdf')
2008 ->withHeader('Content-Disposition', 'attachment; filename="' . $pdf->getFileName() . '"');
2009 $response->write($pdf->download());
2010 return $response;
2011 }
2012 )->setName('emptyAdhesionForm');
2013
2014 //mailing
2015 $app->get(
2016 '/mailing',
2017 function ($request, $response) {
2018 $get = $request->getQueryParams();
2019
2020 //We're done :-)
2021 if (isset($get['mailing_new'])
2022 || isset($get['reminder'])
2023 ) {
2024 if ($this->session->mailing !== null) {
2025 // check for temporary attachments to remove
2026 $m = $this->session->mailing;
2027 $m->removeAttachments(true);
2028 }
2029 $this->session->mailing = null;
2030 }
2031
2032 $params = array();
2033
2034 if ($this->preferences->pref_mail_method == Mailing::METHOD_DISABLED
2035 && !GALETTE_MODE === 'DEMO'
2036 ) {
2037 $this->history->add(
2038 _T("Trying to load mailing while mail is disabled in preferences.")
2039 );
2040 $this->flash->addMessage(
2041 'error_detected',
2042 _T("Trying to load mailing while mail is disabled in preferences.")
2043 );
2044 return $response
2045 ->withStatus(301)
2046 ->withHeader('Location', $this->router->pathFor('slash'));
2047 } else {
2048 if (isset($this->session->filter_mailing)) {
2049 $filters = $this->session->filter_mailing;
2050 } elseif (isset($this->session->filter_members)) {
2051 $filters = $this->session->filter_members;
2052 } else {
2053 $filters = new MembersList();
2054 }
2055
2056 if ($this->session->mailing !== null
2057 && !isset($get['from'])
2058 && !isset($get['reset'])
2059 ) {
2060 $mailing = $this->session->mailing;
2061 } elseif (isset($get['from']) && is_numeric($get['from'])) {
2062 $mailing = new Mailing($this->preferences, null, $get['from']);
2063 MailingHistory::loadFrom($this->zdb, (int)$get['from'], $mailing);
2064 } elseif (isset($get['reminder'])) {
2065 //FIXME: use a constant!
2066 $filters->reinit();
2067 $filters->membership_filter = Members::MEMBERSHIP_LATE;
2068 $filters->filter_account = Members::ACTIVE_ACCOUNT;
2069 $m = new Members($filters);
2070 $members = $m->getList(true);
2071 $mailing = new Mailing($this->preferences, ($members !== false) ? $members : null);
2072 } else {
2073 if (count($filters->selected) == 0
2074 && !isset($get['mailing_new'])
2075 && !isset($get['reminder'])
2076 ) {
2077 Analog::log(
2078 '[Mailings] No member selected for mailing',
2079 Analog::WARNING
2080 );
2081
2082 $this->flash->addMessage(
2083 'error_detected',
2084 _T('No member selected for mailing!')
2085 );
2086
2087 if (isset($profiler)) {
2088 $profiler->stop();
2089 }
2090
2091 $redirect_url = ($this->session->redirect_mailing !== null) ?
2092 $this->session->redirect_mailing :
2093 $this->router->pathFor('members');
2094
2095 return $response
2096 ->withStatus(301)
2097 ->withHeader('Location', $redirect_url);
2098 }
2099 $m = new Members();
2100 $members = $m->getArrayList($filters->selected);
2101 $mailing = new Mailing($this->preferences, ($members !== false) ? $members : null);
2102 }
2103
2104 if (isset($get['remove_attachment'])) {
2105 $mailing->removeAttachment($get['remove_attachment']);
2106 }
2107
2108 if ($mailing->current_step !== Mailing::STEP_SENT) {
2109 $this->session->mailing = $mailing;
2110 }
2111
2112 /** TODO: replace that... */
2113 $this->session->labels = $mailing->unreachables;
2114
2115 if (!$this->login->isSuperAdmin()) {
2116 $member = new Adherent($this->zdb, (int)$this->login->id, false);
2117 $params['sender_current'] = [
2118 'name' => $member->sname,
2119 'email' => $member->getEmail()
2120 ];
2121 }
2122
2123 $params = array_merge(
2124 $params,
2125 array(
2126 'mailing' => $mailing,
2127 'attachments' => $mailing->attachments,
2128 'html_editor' => true,
2129 'html_editor_active'=> $this->preferences->pref_editor_enabled
2130 )
2131 );
2132 }
2133
2134 // display page
2135 $this->view->render(
2136 $response,
2137 'mailing_adherents.tpl',
2138 array_merge(
2139 array(
2140 'page_title' => _T("Mailing"),
2141 'require_dialog' => true
2142 ),
2143 $params
2144 )
2145 );
2146 return $response;
2147 }
2148 )->setName('mailing')->add($authenticate);
2149
2150 $app->post(
2151 '/mailing',
2152 function ($request, $response) {
2153 $post = $request->getParsedBody();
2154 $error_detected = [];
2155 $success_detected = [];
2156
2157 $goto = $this->router->pathFor('mailings');
2158 $redirect_url = ($this->session->redirect_mailing !== null) ?
2159 $this->session->redirect_mailing :
2160 $this->router->pathFor('members');
2161
2162 //We're done :-)
2163 if (isset($post['mailing_done'])
2164 || isset($post['mailing_cancel'])
2165 ) {
2166 if ($this->session->mailing !== null) {
2167 // check for temporary attachments to remove
2168 $m = $this->session->mailing;
2169 $m->removeAttachments(true);
2170 }
2171 $this->session->mailing = null;
2172 if (isset($this->session->filter_mailing)) {
2173 $filters = $this->session->filter_mailing;
2174 $filters->selected = [];
2175 $this->session->filter_mailing = $filters;
2176 }
2177
2178 return $response
2179 ->withStatus(301)
2180 ->withHeader('Location', $redirect_url);
2181 }
2182
2183 $params = array();
2184
2185 if ($this->preferences->pref_mail_method == Mailing::METHOD_DISABLED
2186 && !GALETTE_MODE === 'DEMO'
2187 ) {
2188 $this->history->add(
2189 _T("Trying to load mailing while mail is disabled in preferences.")
2190 );
2191 $error_detected[] = _T("Trying to load mailing while mail is disabled in preferences.");
2192 $goto = $this->router->pathFor('slash');
2193 } else {
2194 if (isset($this->session->filter_members)) {
2195 $filters = $this->session->filter_members;
2196 } else {
2197 $filters = new MembersList();
2198 }
2199
2200 if ($this->session->mailing !== null
2201 && !isset($post['mailing_cancel'])
2202 ) {
2203 $mailing = $this->session->mailing;
2204 } else {
2205 if (count($filters->selected) == 0) {
2206 Analog::log(
2207 '[Mailings] No member selected for mailing',
2208 Analog::WARNING
2209 );
2210
2211 $this->flash->addMessage(
2212 'error_detected',
2213 _T('No member selected for mailing!')
2214 );
2215
2216 return $response
2217 ->withStatus(301)
2218 ->withHeader('Location', $redirect_url);
2219 }
2220 $m = new Members();
2221 $members = $m->getArrayList($filters->selected);
2222 $mailing = new Mailing($this->preferences, ($members !== false) ? $members : null);
2223 }
2224
2225 if (isset($post['mailing_go'])
2226 || isset($post['mailing_reset'])
2227 || isset($post['mailing_confirm'])
2228 || isset($post['mailing_save'])
2229 ) {
2230 if (trim($post['mailing_objet']) == '') {
2231 $error_detected[] = _T("Please type an object for the message.");
2232 } else {
2233 $mailing->subject = $post['mailing_objet'];
2234 }
2235
2236 if (trim($post['mailing_corps']) == '') {
2237 $error_detected[] = _T("Please enter a message.");
2238 } else {
2239 $mailing->message = $post['mailing_corps'];
2240 }
2241
2242 switch ($post['sender']) {
2243 case GaletteMail::SENDER_CURRENT:
2244 $member = new Adherent($this->zdb, (int)$this->login->id, false);
2245 $mailing->setSender(
2246 $member->sname,
2247 $member->getEmail()
2248 );
2249 break;
2250 case GaletteMail::SENDER_OTHER:
2251 $mailing->setSender(
2252 $post['sender_name'],
2253 $post['sender_address']
2254 );
2255 break;
2256 case GaletteMail::SENDER_PREFS:
2257 default:
2258 //nothing to do; this is the default :)
2259 break;
2260 }
2261
2262 $mailing->html = (isset($post['mailing_html'])) ? true : false;
2263
2264 //handle attachments
2265 if (isset($_FILES['files'])) {
2266 for ($i = 0; $i < count($_FILES['files']['name']); $i++) {
2267 if ($_FILES['files']['error'][$i] === UPLOAD_ERR_OK) {
2268 if ($_FILES['files']['tmp_name'][$i] != '') {
2269 if (is_uploaded_file($_FILES['files']['tmp_name'][$i])) {
2270 $da_file = array();
2271 foreach (array_keys($_FILES['files']) as $key) {
2272 $da_file[$key] = $_FILES['files'][$key][$i];
2273 }
2274 $res = $mailing->store($da_file);
2275 if ($res < 0) {
2276 //what to do if one of attachments fail? should other be removed?
2277 $error_detected[] = $mailing->getAttachmentErrorMessage($res);
2278 }
2279 }
2280 }
2281 } elseif ($_FILES['files']['error'][$i] !== UPLOAD_ERR_NO_FILE) {
2282 Analog::log(
2283 $this->logo->getPhpErrorMessage($_FILES['files']['error'][$i]),
2284 Analog::WARNING
2285 );
2286 $error_detected[] = $this->logo->getPhpErrorMessage(
2287 $_FILES['files']['error'][$i]
2288 );
2289 }
2290 }
2291 }
2292
2293 if (count($error_detected) == 0
2294 && !isset($post['mailing_reset'])
2295 && !isset($post['mailing_save'])
2296 ) {
2297 $mailing->current_step = Mailing::STEP_PREVIEW;
2298 } else {
2299 $mailing->current_step = Mailing::STEP_START;
2300 }
2301 }
2302
2303 if (isset($post['mailing_confirm']) && count($error_detected) == 0) {
2304 $mailing->current_step = Mailing::STEP_SEND;
2305 //ok... let's go for fun
2306 $sent = $mailing->send();
2307 if ($sent == Mailing::MAIL_ERROR) {
2308 $mailing->current_step = Mailing::STEP_START;
2309 Analog::log(
2310 '[Mailings] Message was not sent. Errors: ' .
2311 print_r($mailing->errors, true),
2312 Analog::ERROR
2313 );
2314 foreach ($mailing->errors as $e) {
2315 $error_detected[] = $e;
2316 }
2317 } else {
2318 $mlh = new MailingHistory($this->zdb, $this->login, null, $mailing);
2319 $mlh->storeMailing(true);
2320 Analog::log(
2321 '[Mailings] Message has been sent.',
2322 Analog::INFO
2323 );
2324 $mailing->current_step = Mailing::STEP_SENT;
2325 //cleanup
2326 $filters->selected = null;
2327 $this->session->filter_members = $filters;
2328 $this->session->mailing = null;
2329 $success_detected[] = _T("Mailing has been successfully sent!");
2330 $goto = $redirect_url;
2331 }
2332 }
2333
2334 if ($mailing->current_step !== Mailing::STEP_SENT) {
2335 $this->session->mailing = $mailing;
2336 }
2337
2338 /** TODO: replace that... */
2339 $this->session->labels = $mailing->unreachables;
2340
2341 if (!isset($post['html_editor_active'])
2342 || trim($post['html_editor_active']) == ''
2343 ) {
2344 $post['html_editor_active'] = $this->preferences->pref_editor_enabled;
2345 }
2346
2347 if (isset($post['mailing_save'])) {
2348 //user requested to save the mailing
2349 $histo = new MailingHistory($this->zdb, $this->login, null, $mailing);
2350 if ($histo->storeMailing() !== false) {
2351 $success_detected[] = _T("Mailing has been successfully saved.");
2352 $this->session->mailing = null;
2353 }
2354 }
2355 }
2356
2357 //flash messages if any
2358 if (count($error_detected) > 0) {
2359 foreach ($error_detected as $error) {
2360 $this->flash->addMessage('error_detected', $error);
2361 }
2362 }
2363 if (count($success_detected) > 0) {
2364 foreach ($success_detected as $success) {
2365 $this->flash->addMessage('success_detected', $success);
2366 }
2367 }
2368
2369 return $response
2370 ->withStatus(301)
2371 ->withHeader('Location', $goto);
2372 }
2373 )->setName('doMailing')->add($authenticate);
2374
2375 $app->map(
2376 ['GET', 'POST'],
2377 '/mailing/preview[/{id:\d+}]',
2378 function ($request, $response, $args) {
2379 $post = $request->getParsedBody();
2380 // check for ajax mode
2381 $ajax = false;
2382 if ($request->isXhr()
2383 || isset($post['ajax'])
2384 && $post['ajax'] == 'true'
2385 ) {
2386 $ajax = true;
2387 }
2388
2389 $mailing = null;
2390 if (isset($args['id'])) {
2391 $mailing = new Mailing($this->preferences, null);
2392 MailingHistory::loadFrom($this->zdb, (int)$args['id'], $mailing, false);
2393 $attachments = $mailing->attachments;
2394 } else {
2395 $mailing = $this->session->mailing;
2396
2397 switch ($post['sender']) {
2398 case GaletteMail::SENDER_CURRENT:
2399 $member = new Adherent($this->zdb, (int)$this->login->id, false);
2400 $mailing->setSender(
2401 $member->sname,
2402 $member->getEmail()
2403 );
2404 break;
2405 case GaletteMail::SENDER_OTHER:
2406 $mailing->setSender(
2407 $post['sender_name'],
2408 $post['sender_address']
2409 );
2410 break;
2411 case GaletteMail::SENDER_PREFS:
2412 default:
2413 //nothing to do; this is the default :)
2414 break;
2415 }
2416
2417 $mailing->subject = $post['subject'];
2418 $mailing->message = $post['body'];
2419 $mailing->html = ($post['html'] === 'true');
2420 $attachments = (isset($post['attachments']) ? $post['attachments'] : []);
2421 }
2422
2423 // display page
2424 $this->view->render(
2425 $response,
2426 'mailing_preview.tpl',
2427 [
2428 'page_title' => _T("Mailing preview"),
2429 'mailing_id' => $args['id'],
2430 'mode' => ($ajax ? 'ajax' : ''),
2431 'mailing' => $mailing,
2432 'recipients' => $mailing->recipients,
2433 'sender' => $mailing->getSenderName() . ' &lt;' .
2434 $mailing->getSenderAddress() . '&gt;',
2435 'attachments' => $attachments
2436
2437 ]
2438 );
2439 return $response;
2440 }
2441 )->setName('mailingPreview')->add($authenticate);
2442
2443 $app->get(
2444 '/mailing/preview/{id:\d+}/attachment/{pos:\d+}',
2445 function ($request, $response, $args) {
2446 $mailing = new Mailing($this->preferences, null);
2447 MailingHistory::loadFrom($this->zdb, (int)$args['id'], $mailing, false);
2448 $attachments = $mailing->attachments;
2449 $attachment = $attachments[$args['pos']];
2450 $filepath = $attachment->getDestDir() . $attachment->getFileName();
2451
2452
2453 $ext = pathinfo($attachment->getFileName())['extension'];
2454 $response = $response->withHeader('Content-type', $attachment->getMimeType($filepath));
2455
2456 $body = $response->getBody();
2457 $body->write(file_get_contents($filepath));
2458 return $response;
2459 }
2460 )->setName('previewAttachment')->add($authenticate);
2461
2462 $app->post(
2463 '/ajax/mailing/set-recipients',
2464 function ($request, $response, $args) {
2465 $post = $request->getParsedBody();
2466 $mailing = $this->session->mailing;
2467
2468 $m = new Members();
2469
2470 $members = $m->getArrayList(
2471 $post['recipients'],
2472 null,
2473 false,
2474 true,
2475 null,
2476 false,
2477 false,
2478 true
2479 );
2480 $mailing->setRecipients($members);
2481
2482 $this->session->mailing = $mailing;
2483
2484 // display page
2485 $this->view->render(
2486 $response,
2487 'mailing_recipients.tpl',
2488 [
2489 'mailing' => $mailing
2490
2491 ]
2492 );
2493 return $response;
2494 }
2495 )->setName('mailingRecipients')->add($authenticate);
2496
2497 //reminders
2498 $app->get(
2499 '/reminders',
2500 function ($request, $response) {
2501 $texts = new Texts($this->texts_fields, $this->preferences, $this->router);
2502
2503 $previews = array(
2504 'impending' => $texts->getTexts('impendingduedate', $this->preferences->pref_lang),
2505 'late' => $texts->getTexts('lateduedate', $this->preferences->pref_lang)
2506 );
2507
2508 $members = new Members();
2509 $reminders = $members->getRemindersCount();
2510
2511 // display page
2512 $this->view->render(
2513 $response,
2514 'reminder.tpl',
2515 [
2516 'page_title' => _T("Reminders"),
2517 'previews' => $previews,
2518 'require_dialog' => true,
2519 'count_impending' => $reminders['impending'],
2520 'count_impending_nomail' => $reminders['nomail']['impending'],
2521 'count_late' => $reminders['late'],
2522 'count_late_nomail' => $reminders['nomail']['late']
2523 ]
2524 );
2525 return $response;
2526 }
2527 )->setName('reminders')->add($authenticate);
2528
2529 $app->post(
2530 '/reminders',
2531 function ($request, $response) {
2532 $error_detected = [];
2533 $warning_detected = [];
2534 $success_detected = [];
2535
2536 $post = $request->getParsedBody();
2537 $texts = new Texts($this->texts_fields, $this->preferences, $this->router);
2538 $selected = null;
2539 if (isset($post['reminders'])) {
2540 $selected = $post['reminders'];
2541 }
2542 $reminders = new Reminders($selected);
2543
2544 $labels = false;
2545 $labels_members = array();
2546 if (isset($post['reminder_wo_mail'])) {
2547 $labels = true;
2548 }
2549
2550 $list_reminders = $reminders->getList($this->zdb, $labels);
2551 if (count($list_reminders) == 0) {
2552 $warning_detected[] = _T("No reminder to send for now.");
2553 } else {
2554 foreach ($list_reminders as $reminder) {
2555 if ($labels === false) {
2556 //send reminders by mail
2557 $sent = $reminder->send($texts, $this->history, $this->zdb);
2558
2559 if ($sent === true) {
2560 $success_detected[] = $reminder->getMessage();
2561 } else {
2562 $error_detected[] = $reminder->getMessage();
2563 }
2564 } else {
2565 //generate labels for members without mail address
2566 $labels_members[] = $reminder->member_id;
2567 }
2568 }
2569
2570 if ($labels === true) {
2571 if (count($labels_members) > 0) {
2572 $labels_filters = new MembersList();
2573 $labels_filters->selected = $labels_members;
2574 $this->session->filters_reminders_labels = $labels_filters;
2575 return $response
2576 ->withStatus(301)
2577 ->withHeader('Location', $this->router->pathFor('pdf-member-labels'));
2578 } else {
2579 $error_detected[] = _T("There are no member to proceed.");
2580 }
2581 }
2582
2583 if (count($error_detected) > 0) {
2584 array_unshift(
2585 $error_detected,
2586 _T("Reminder has not been sent:")
2587 );
2588 }
2589
2590 if (count($success_detected) > 0) {
2591 array_unshift(
2592 $success_detected,
2593 _T("Sent reminders:")
2594 );
2595 }
2596 }
2597
2598 //flash messages if any
2599 if (count($error_detected) > 0) {
2600 foreach ($error_detected as $error) {
2601 $this->flash->addMessage('error_detected', $error);
2602 }
2603 }
2604 if (count($warning_detected) > 0) {
2605 foreach ($warning_detected as $warning) {
2606 $this->flash->addMessage('warning_detected', $warning);
2607 }
2608 }
2609 if (count($success_detected) > 0) {
2610 foreach ($success_detected as $success) {
2611 $this->flash->addMessage('success_detected', $success);
2612 }
2613 }
2614
2615 return $response
2616 ->withStatus(301)
2617 ->withHeader('Location', $this->router->pathFor('reminders'));
2618 }
2619 )->setName('doReminders')->add($authenticate);
2620
2621 $app->get(
2622 '/members/reminder-filter/{membership:nearly|late}/{mail:withmail|withoutmail}',
2623 function ($request, $response, $args) {
2624 //always reset filters
2625 $filters = new MembersList();
2626 $filters->filter_account = Members::ACTIVE_ACCOUNT;
2627
2628 $membership = ($args['membership'] === 'nearly' ?
2629 Members::MEMBERSHIP_NEARLY :
2630 Members::MEMBERSHIP_LATE);
2631 $filters->membership_filter = $membership;
2632
2633 //TODO: filter on reminder may take care of parent email as well
2634 $mail = ($args['mail'] === 'withmail' ?
2635 Members::FILTER_W_EMAIL :
2636 Members::FILTER_WO_EMAIL);
2637 $filters->email_filter = $mail;
2638
2639 $this->session->filter_members = $filters;
2640
2641 return $response
2642 ->withStatus(301)
2643 ->withHeader('Location', $this->router->pathFor('members'));
2644 }
2645 )->setName('reminders-filter')->add($authenticate);
2646
2647 $app->map(
2648 ['GET', 'POST'],
2649 '/attendance-sheet/details',
2650 function ($request, $response) {
2651 $post = $request->getParsedBody();
2652
2653 if ($this->session->filter_members !== null) {
2654 $filters = $this->session->filter_members;
2655 } else {
2656 $filters = new MembersList();
2657 }
2658
2659 // check for ajax mode
2660 $ajax = false;
2661 if ($request->isXhr()
2662 || isset($post['ajax'])
2663 && $post['ajax'] == 'true'
2664 ) {
2665 $ajax = true;
2666
2667 //retrieve selected members
2668 $selection = (isset($post['selection']) ) ? $post['selection'] : array();
2669
2670 $filters->selected = $selection;
2671 $this->session->filter_members = $filters;
2672 } else {
2673 $selection = $filters->selected;
2674 }
2675
2676
2677 // display page
2678 $this->view->render(
2679 $response,
2680 'attendance_sheet_details.tpl',
2681 [
2682 'page_title' => _T("Attendance sheet configuration"),
2683 'ajax' => $ajax,
2684 'selection' => $selection
2685 ]
2686 );
2687 return $response;
2688 }
2689 )->setName('attendance_sheet_details')->add($authenticate);
2690
2691 $app->post(
2692 '/attendance-sheet',
2693 function ($request, $response) {
2694 $post = $request->getParsedBody();
2695
2696 if ($this->session->filter_members !== null) {
2697 $filters = $this->session->filter_members;
2698 } else {
2699 $filters = new MembersList();
2700 }
2701
2702 //retrieve selected members
2703 $selection = (isset($post['selection']) ) ? $post['selection'] : array();
2704
2705 $filters->selected = $selection;
2706 $this->session->filter_members = $filters;
2707
2708 if (count($filters->selected) == 0) {
2709 Analog::log('No member selected to generate attendance sheet', Analog::INFO);
2710 $this->flash->addMessage(
2711 'error_detected',
2712 _T("No member selected to generate attendance sheet")
2713 );
2714
2715 return $response
2716 ->withStatus(301)
2717 ->withHeader('Location', $this->router->pathFor('members'));
2718 }
2719
2720 $m = new Members();
2721 $members = $m->getArrayList(
2722 $filters->selected,
2723 array('nom_adh', 'prenom_adh'),
2724 true
2725 );
2726
2727 if (!is_array($members) || count($members) < 1) {
2728 Analog::log('No member selected to generate attendance sheet', Analog::INFO);
2729 $this->flash->addMessage(
2730 'error_detected',
2731 _T("No member selected to generate attendance sheet")
2732 );
2733
2734 return $response
2735 ->withStatus(301)
2736 ->withHeader('Location', $this->router->pathFor('members'));
2737 }
2738
2739 $doc_title = _T("Attendance sheet");
2740 if (isset($post['sheet_type']) && trim($post['sheet_type']) != '') {
2741 $doc_title = $post['sheet_type'];
2742 }
2743
2744 $pdf = new Galette\IO\PdfAttendanceSheet($this->preferences);
2745 $pdf->doc_title = $doc_title;
2746 // Set document information
2747 $pdf->SetTitle($doc_title);
2748
2749 if (isset($post['sheet_title']) && trim($post['sheet_title']) != '') {
2750 $pdf->sheet_title = $post['sheet_title'];
2751 }
2752 if (isset($post['sheet_sub_title']) && trim($post['sheet_sub_title']) != '') {
2753 $pdf->sheet_sub_title = $post['sheet_sub_title'];
2754 }
2755 if (isset($post['sheet_date']) && trim($post['sheet_date']) != '') {
2756 $dformat = __("Y-m-d");
2757 $date = DateTime::createFromFormat(
2758 $dformat,
2759 $post['sheet_date']
2760 );
2761 $pdf->sheet_date = $date;
2762 }
2763 //with or without images?
2764 if (isset($post['sheet_photos']) && $post['sheet_photos'] === '1') {
2765 $pdf->withImages();
2766 }
2767 $pdf->drawSheet($members, $doc_title);
2768 $response = $this->response->withHeader('Content-type', 'application/pdf');
2769 $response->write($pdf->Output(_T("attendance_sheet") . '.pdf', 'D'));
2770 return $response;
2771 }
2772 )->setName('attendance_sheet')->add($authenticate);
2773
2774 $app->post(
2775 '/ajax/members[/{option:page|order}/{value:\d+}]',
2776 function ($request, $response, $args) {
2777 $post = $request->getParsedBody();
2778
2779 if (isset($this->session->ajax_members_filters)) {
2780 $filters = $this->session->ajax_members_filters;
2781 } else {
2782 $filters = new MembersList();
2783 }
2784
2785 if (isset($args['option']) && $args['option'] == 'page') {
2786 $filters->current_page = (int)$args['value'];
2787 }
2788
2789 //numbers of rows to display
2790 if (isset($post['nbshow']) && is_numeric($post['nbshow'])) {
2791 $filters->show = $post['nbshow'];
2792 }
2793
2794 $members = new Members($filters);
2795 if (!$this->login->isAdmin() && !$this->login->isStaff()) {
2796 if ($this->login->isGroupManager()) {
2797 $members_list = $members->getManagedMembersList(true);
2798 } else {
2799 Analog::log(
2800 str_replace(
2801 ['%id', '%login'],
2802 [$this->login->id, $this->login->login],
2803 'Trying to list group members without access from #%id (%login)'
2804 ),
2805 Analog::ERROR
2806 );
2807 throw new Exception('Access denied.');
2808 exit(0);
2809 }
2810 } else {
2811 $members_list = $members->getMembersList(true);
2812 }
2813
2814 //assign pagination variables to the template and add pagination links
2815 $filters->setSmartyPagination($this->router, $this->view->getSmarty(), false);
2816
2817 $this->session->ajax_members_filters = $filters;
2818
2819 $selected_members = null;
2820 $unreachables_members = null;
2821 if (!isset($post['from'])) {
2822 $mailing = $this->session->mailing;
2823 if (!isset($post['members'])) {
2824 $selected_members = $mailing->recipients;
2825 $unreachables_members = $mailing->unreachables;
2826 } else {
2827 $m = new Members();
2828 $selected_members = $m->getArrayList($post['members']);
2829 if (isset($post['unreachables']) && is_array($post['unreachables'])) {
2830 $unreachables_members = $m->getArrayList($post['unreachables']);
2831 }
2832 }
2833 } else {
2834 switch ($post['from']) {
2835 case 'groups':
2836 if (!isset($post['gid'])) {
2837 Analog::log(
2838 'Trying to list group members with no group id provided',
2839 Analog::ERROR
2840 );
2841 throw new Exception('A group id is required.');
2842 exit(0);
2843 }
2844 if (!isset($post['members'])) {
2845 $group = new Group((int)$post['gid']);
2846 $selected_members = array();
2847 if (!isset($post['mode']) || $post['mode'] == 'members') {
2848 $selected_members = $group->getMembers();
2849 } elseif ($post['mode'] == 'managers') {
2850 $selected_members = $group->getManagers();
2851 } else {
2852 Analog::log(
2853 'Trying to list group members with unknown mode',
2854 Analog::ERROR
2855 );
2856 throw new Exception('Unknown mode.');
2857 exit(0);
2858 }
2859 } else {
2860 $m = new Members();
2861 $selected_members = $m->getArrayList($post['members']);
2862 if (isset($post['unreachables']) && is_array($post['unreachables'])) {
2863 $unreachables_members = $m->getArrayList($post['unreachables']);
2864 }
2865 }
2866 break;
2867 case 'attach':
2868 if (!isset($post['id_adh'])) {
2869 throw new \RuntimeException(
2870 'Current selected member must be excluded while attaching!'
2871 );
2872 exit(0);
2873 }
2874 break;
2875 }
2876 }
2877
2878 $params = [
2879 'filters' => $filters,
2880 'members_list' => $members_list,
2881 'selected_members' => $selected_members,
2882 'unreachables_members' => $unreachables_members
2883 ];
2884
2885 if (isset($post['multiple'])) {
2886 $params['multiple'] = true;
2887 }
2888
2889 if (isset($post['gid'])) {
2890 $params['the_id'] = $post['gid'];
2891 }
2892
2893 if (isset($post['id_adh'])) {
2894 $params['excluded'] = $post['id_adh'];
2895 }
2896
2897 // display page
2898 $this->view->render(
2899 $response,
2900 'ajax_members.tpl',
2901 $params
2902 );
2903 return $response;
2904 }
2905 )->setName('ajaxMembers')->add($authenticate);
2906
2907 $app->post(
2908 '/ajax/group/members',
2909 function ($request, $response) {
2910 $post = $request->getParsedBody();
2911
2912 $ids = $post['persons'];
2913 $mode = $post['person_mode'];
2914
2915 if (!$ids || !$mode) {
2916 Analog::log(
2917 'Missing persons and mode for ajaxGroupMembers',
2918 Analog::INFO
2919 );
2920 die();
2921 }
2922
2923 $m = new Members;
2924 $persons = $m->getArrayList($ids);
2925
2926 // display page
2927 $this->view->render(
2928 $response,
2929 'group_persons.tpl',
2930 [
2931 'persons' => $persons,
2932 'person_mode' => $mode
2933 ]
2934 );
2935 return $response;
2936 }
2937 )->setName('ajaxGroupMembers')->add($authenticate);
2938
2939 $app->get(
2940 '/member/{id:\d+}/file/{fid:\d+}/{pos:\d+}/{name}',
2941 function ($request, $response, $args) {
2942 $denied = false;
2943 $id = (int)$args['id'];
2944 if ($this->login->id != $args['id']
2945 && !$this->login->isAdmin()
2946 && !$this->login->isStaff()
2947 && !$this->login->isGroupManager()
2948 ) {
2949 $denied = true;
2950 }
2951
2952 $deps = array(
2953 'picture' => false,
2954 'groups' => false,
2955 'dues' => false,
2956 'parent' => false,
2957 'children' => false,
2958 'dynamics' => true
2959 );
2960 $member = new Adherent($this->zdb, $id, $deps);
2961
2962 if (!$denied && $this->login->id != $args['id']
2963 && $this->login->isGroupManager()
2964 && !$this->login->isStaff()
2965 && !$this->login->isAdmin()
2966 ) {
2967 //check if current logged in user can manage loaded member
2968 $groups = $member->groups;
2969 $can_manage = false;
2970 foreach ($groups as $group) {
2971 if ($this->login->isGroupManager($group->getId())) {
2972 $can_manage = true;
2973 break;
2974 }
2975 }
2976 if ($can_manage !== true) {
2977 Analog::log(
2978 'Logged in member ' . $this->login->login .
2979 ' has tried to load member #' . $member->id .
2980 ' but do not manage any groups he belongs to.',
2981 Analog::WARNING
2982 );
2983 $denied = true;
2984 }
2985 }
2986
2987 if ($denied === false) {
2988 $fields = $member->getDynamicFields()->getFields();
2989 if (!isset($fields[$args['fid']])) {
2990 //field does not exists or access is forbidden
2991 $denied = true;
2992 }
2993 }
2994
2995 if ($denied === true) {
2996 $this->flash->addMessage(
2997 'error_detected',
2998 _T("You do not have permission for requested URL.")
2999 );
3000
3001 return $response
3002 ->withStatus(403)
3003 ->withHeader(
3004 'Location',
3005 $this->router->pathFor(
3006 'member',
3007 ['id' => $id]
3008 )
3009 );
3010 }
3011
3012 $filename = str_replace(
3013 [
3014 '%mid',
3015 '%fid',
3016 '%pos'
3017 ],
3018 [
3019 $args['id'],
3020 $args['fid'],
3021 $args['pos']
3022 ],
3023 'member_%mid_field_%fid_value_%pos'
3024 );
3025
3026 if (file_exists(GALETTE_FILES_PATH . $filename)) {
3027 $type = File::getMimeType(GALETTE_FILES_PATH . $filename);
3028 $response = $this->response
3029 ->withHeader('Content-Type', $type)
3030 ->withHeader('Content-Disposition', 'attachment;filename="' . $args['name'] . '"')
3031 ->withHeader('Pragma', 'no-cache');
3032 $response->write(readfile(GALETTE_FILES_PATH . $filename));
3033 return $response;
3034 } else {
3035 Analog::log(
3036 'A request has been made to get an exported file named `' .
3037 $filename .'` that does not exists.',
3038 Analog::WARNING
3039 );
3040
3041 $this->flash->addMessage(
3042 'error_detected',
3043 _T("The file does not exists or cannot be read :(")
3044 );
3045
3046 return $response
3047 ->withStatus(404)
3048 ->withHeader(
3049 'Location',
3050 $this->router->pathFor('member', ['id' => $args['id']])
3051 );
3052 }
3053 }
3054 )->setName('getDynamicFile')->add($authenticate);
3055
3056 $app->get(
3057 '/members/mass-change',
3058 function ($request, $response) {
3059 $filters = $this->session->filter_members;
3060
3061 $data = [
3062 'id' => $filters->selected,
3063 'redirect_uri' => $this->router->pathFor('members')
3064 ];
3065
3066 $fc = $this->fields_config;
3067 $form_elements = $fc->getMassiveFormElements($this->members_fields, $this->login);
3068
3069 //dynamic fields
3070 $deps = array(
3071 'picture' => false,
3072 'groups' => false,
3073 'dues' => false,
3074 'parent' => false,
3075 'children' => false,
3076 'dynamics' => false
3077 );
3078 $member = new Adherent($this->zdb, null, $deps);
3079
3080 //Status
3081 $statuts = new Status($this->zdb);
3082
3083 // display page
3084 $this->view->render(
3085 $response,
3086 'mass_change_members.tpl',
3087 array(
3088 'mode' => $request->isXhr() ? 'ajax' : '',
3089 'page_title' => str_replace(
3090 '%count',
3091 count($data['id']),
3092 _T('Mass change %count members')
3093 ),
3094 'form_url' => $this->router->pathFor('masschangeMembersReview'),
3095 'cancel_uri' => $this->router->pathFor('members'),
3096 'data' => $data,
3097 'member' => $member,
3098 'fieldsets' => $form_elements['fieldsets'],
3099 'titles_list' => Titles::getList($this->zdb),
3100 'statuts' => $statuts->getList(),
3101 'require_mass' => true
3102 )
3103 );
3104 return $response;
3105 }
3106 )->setName('masschangeMembers')->add($authenticate);
3107
3108 $app->post(
3109 '/members/mass-change/validate',
3110 function ($request, $response) {
3111 $post = $request->getParsedBody();
3112
3113 if (!isset($post['confirm'])) {
3114 $this->flash->addMessage(
3115 'error_detected',
3116 _T("Mass changes has not been confirmed!")
3117 );
3118 } else {
3119 //we want only visibles fields
3120 $fc = $this->fields_config;
3121 $form_elements = $fc->getMassiveFormElements($this->members_fields, $this->login);
3122
3123 $changes = [];
3124 foreach ($form_elements['fieldsets'] as $form_element) {
3125 foreach ($form_element->elements as $field) {
3126 if (isset($post[$field->field_id]) && isset($post['mass_' . $field->field_id])) {
3127 $changes[$field->field_id] = [
3128 'label' => $field->label,
3129 'value' => $post[$field->field_id]
3130 ];
3131 }
3132 }
3133 }
3134 }
3135
3136 $filters = $this->session->filter_members;
3137 $data = [
3138 'id' => $filters->selected,
3139 'redirect_uri' => $this->router->pathFor('members')
3140 ];
3141
3142 //Status
3143 $statuts = new Status($this->zdb);
3144
3145 // display page
3146 $this->view->render(
3147 $response,
3148 'mass_change_members.tpl',
3149 array(
3150 'mode' => $request->isXhr() ? 'ajax' : '',
3151 'page_title' => str_replace(
3152 '%count',
3153 count($data['id']),
3154 _T('Review mass change %count members')
3155 ),
3156 'form_url' => $this->router->pathFor('massstoremembers'),
3157 'cancel_uri' => $this->router->pathFor('members'),
3158 'data' => $data,
3159 'titles_list' => Titles::getList($this->zdb),
3160 'statuts' => $statuts->getList(),
3161 'changes' => $changes
3162 )
3163 );
3164 return $response;
3165 }
3166 )->setName('masschangeMembersReview')->add($authenticate);
3167
3168 $app->post(
3169 '/members/mass-change',
3170 function ($request, $response, $args) {
3171 $post = $request->getParsedBody();
3172 $redirect_url = $post['redirect_uri'];
3173 $error_detected = [];
3174 $mass = 0;
3175
3176 unset($post['redirect_uri']);
3177 if (!isset($post['confirm'])) {
3178 $error_detected[] = _T("Mass changes has not been confirmed!");
3179 } else {
3180 unset($post['confirm']);
3181 $ids = $post['id'];
3182 unset($post['id']);
3183
3184 $fc = $this->fields_config;
3185 $form_elements = $fc->getMassiveFormElements($this->members_fields, $this->login);
3186 $disabled = $this->members_fields;
3187 foreach (array_keys($post) as $key) {
3188 $found = false;
3189 foreach ($form_elements['fieldsets'] as $fieldset) {
3190 if (isset($fieldset->elements[$key])) {
3191 $found = true;
3192 continue;
3193 }
3194 }
3195 if (!$found) {
3196 Analog::log(
3197 'Permission issue mass editing field ' . $key,
3198 Analog::WARNING
3199 );
3200 unset($post[$key]);
3201 } else {
3202 unset($disabled[$key]);
3203 }
3204 }
3205
3206 if (!count($post)) {
3207 $error_detected[] = _T("Nothing to do!");
3208 } else {
3209 $is_manager = !$this->login->isAdmin() && !$this->login->isStaff() && $this->login->isGroupManager();
3210 foreach ($ids as $id) {
3211 $deps = array(
3212 'picture' => false,
3213 'groups' => $is_manager,
3214 'dues' => false,
3215 'parent' => false,
3216 'children' => false,
3217 'dynamics' => false
3218 );
3219 $member = new Adherent($this->zdb, (int)$id, $deps);
3220 $member->setDependencies(
3221 $this->preferences,
3222 $this->members_fields,
3223 $this->history
3224 );
3225
3226 if ($is_manager) {
3227 $groups = $member->groups;
3228 $is_managed = false;
3229 foreach ($groups as $g) {
3230 if ($this->login->isGroupManager($g->getId())) {
3231 $is_managed = true;
3232 break;
3233 }
3234 }
3235 if (!$is_managed) {
3236 Analog::log(
3237 'Trying to edit member #' . $id . ' without appropriate ACLs',
3238 Analog::WARNING
3239 );
3240 $error_detected[] = _T('No permission to edit member');
3241 continue;
3242 }
3243 }
3244
3245 $valid = $member->check($post, [], $disabled);
3246 if ($valid === true) {
3247 $done = $member->store();
3248 if (!$done) {
3249 $error_detected[] = _T("An error occurred while storing the member.");
3250 } else {
3251 ++$mass;
3252 }
3253 } else {
3254 $error_detected = array_merge($error_detected, $valid);
3255 }
3256 }
3257 }
3258 }
3259
3260 if ($mass == 0 && !count($error_detected)) {
3261 $error_detected[] = _T('Something went wront during mass edition!');
3262 } else {
3263 $this->flash->addMessage(
3264 'success_detected',
3265 str_replace(
3266 '%count',
3267 $mass,
3268 _T('%count members has been changed successfully!')
3269 )
3270 );
3271 }
3272
3273 if (count($error_detected) > 0) {
3274 foreach ($error_detected as $error) {
3275 $this->flash->addMessage(
3276 'error_detected',
3277 $error
3278 );
3279 }
3280 }
3281
3282 if (!$request->isXhr()) {
3283 return $response
3284 ->withStatus(301)
3285 ->withHeader('Location', $redirect_url);
3286 } else {
3287 return $response->withJson(
3288 [
3289 'success' => count($error_detected) === 0
3290 ]
3291 );
3292 }
3293 }
3294 )->setName('massstoremembers')->add($authenticate);
3295
3296 //Duplicate member
3297 $app->get(
3298 '/members/duplicate/{' . Adherent::PK . ':\d+}',
3299 function ($request, $response, $args) {
3300 $id_adh = (int)$args[Adherent::PK];
3301 $adh = new Adherent($this->zdb, $id_adh, ['dynamics' => true]);
3302 $adh->setDuplicate();
3303
3304 //store entity in session
3305 $this->session->member = $adh;
3306
3307 return $response
3308 ->withStatus(301)
3309 ->withHeader('Location', $this->router->pathFor('editmember', ['action' => 'add']));
3310 }
3311 )->setName('duplicateMember')->add($authenticate);
3312
3313 //saved searches
3314 $app->map(
3315 ['GET', 'POST'],
3316 '/save-search',
3317 function ($request, $response) {
3318 if ($request->isPost()) {
3319 $post = $request->getParsedBody();
3320 } else {
3321 $post = $request->getQueryParams();
3322 }
3323
3324 $name = null;
3325 if (isset($post['search_title'])) {
3326 $name = $post['search_title'];
3327 unset($post['search_title']);
3328 }
3329
3330 //when using advanced search, no parameters are sent
3331 if (isset($post['advanced_search'])) {
3332 $post = [];
3333 $filters = $this->session->filter_members;
3334 foreach ($filters->search_fields as $field) {
3335 $post[$field] = $filters->$field;
3336 }
3337 }
3338
3339 //reformat, add required infos
3340 $post = [
3341 'parameters' => $post,
3342 'form' => 'Adherent',
3343 'name' => $name
3344 ];
3345
3346 $sco = new Galette\Entity\SavedSearch($this->zdb, $this->login);
3347 if ($check = $sco->check($post)) {
3348 if (!$res = $sco->store()) {
3349 if ($res === false) {
3350 $this->flash->addMessage(
3351 'error_detected',
3352 _T("An SQL error has occurred while storing search.")
3353 );
3354 } else {
3355 $this->flash->addMessage(
3356 'warning_detected',
3357 _T("This search is already saved.")
3358 );
3359 }
3360 } else {
3361 $this->flash->addMessage(
3362 'success_detected',
3363 _T("Search has been saved.")
3364 );
3365 }
3366 } else {
3367 //report errors
3368 foreach ($sco->getErrors() as $error) {
3369 $this->flash->addMessage(
3370 'error_detected',
3371 $error
3372 );
3373 }
3374 }
3375
3376 if ($request->isGet()) {
3377 return $response
3378 ->withStatus(301)
3379 ->withHeader('Location', $this->router->pathFor('members'));
3380 }
3381 }
3382 )->setName('saveSearch');
3383
3384 $app->get(
3385 '/saved-searches[/{option:page|order}/{value:\d+}]',
3386 function ($request, $response, $args = []) {
3387 $option = null;
3388 if (isset($args['option'])) {
3389 $option = $args['option'];
3390 }
3391 $value = null;
3392 if (isset($args['value'])) {
3393 $value = $args['value'];
3394 }
3395
3396 if (isset($this->session->filter_savedsearch)) {
3397 $filters = $this->session->filter_savedsearch;
3398 } else {
3399 $filters = new SavedSearchesList();
3400 }
3401
3402 if ($option !== null) {
3403 switch ($option) {
3404 case 'page':
3405 $filters->current_page = (int)$value;
3406 break;
3407 case 'order':
3408 $filters->orderby = $value;
3409 break;
3410 }
3411 }
3412
3413 $searches = new SavedSearches($this->zdb, $this->login, $filters);
3414 $list = $searches->getList(true);
3415
3416 //assign pagination variables to the template and add pagination links
3417 $filters->setSmartyPagination($this->router, $this->view->getSmarty(), false);
3418
3419 $this->session->filter_savedsearch = $filters;
3420
3421 // display page
3422 $this->view->render(
3423 $response,
3424 'saved_searches.tpl',
3425 array(
3426 'page_title' => _T("Saved searches"),
3427 'require_dialog' => true,
3428 'searches' => $list,
3429 'nb' => $searches->getCount(),
3430 'filters' => $filters
3431 )
3432 );
3433 return $response;
3434 }
3435 )->setName('searches')->add($authenticate);
3436
3437 $app->get(
3438 '/search/remove/{id:\d+}',
3439 function ($request, $response, $args) {
3440 $data = [
3441 'id' => $args['id'],
3442 'redirect_uri' => $this->router->pathFor('searches')
3443 ];
3444
3445 // display page
3446 $this->view->render(
3447 $response,
3448 'confirm_removal.tpl',
3449 array(
3450 'type' => _T("Saved search"),
3451 'mode' => $request->isXhr() ? 'ajax' : '',
3452 'page_title' => _T('Remove saved search'),
3453 'form_url' => $this->router->pathFor('doRemoveSearch', ['id' => $args['id']]),
3454 'cancel_uri' => $this->router->pathFor('searches'),
3455 'data' => $data
3456 )
3457 );
3458 return $response;
3459 }
3460 )->setName('removeSearch')->add($authenticate);
3461
3462 $app->get(
3463 '/searches/remove',
3464 function ($request, $response) {
3465 $filters = $this->session->filter_savedsearch;
3466
3467 $data = [
3468 'id' => $filters->selected,
3469 'redirect_uri' => $this->router->pathFor('searches')
3470 ];
3471
3472 // display page
3473 $this->view->render(
3474 $response,
3475 'confirm_removal.tpl',
3476 array(
3477 'type' => _T("Saved search"),
3478 'mode' => $request->isXhr() ? 'ajax' : '',
3479 'page_title' => _T('Remove saved searches'),
3480 'message' => str_replace(
3481 '%count',
3482 count($data['id']),
3483 _T('You are about to remove %count searches.')
3484 ),
3485 'form_url' => $this->router->pathFor('doRemoveSearch'),
3486 'cancel_uri' => $this->router->pathFor('searches'),
3487 'data' => $data
3488 )
3489 );
3490 return $response;
3491 }
3492 )->setName('removeSearches')->add($authenticate);
3493
3494 $app->post(
3495 '/search/remove' . '[/{id:\d+}]',
3496 function ($request, $response) {
3497 $post = $request->getParsedBody();
3498 $ajax = isset($post['ajax']) && $post['ajax'] === 'true';
3499 $success = false;
3500
3501 $uri = isset($post['redirect_uri']) ?
3502 $post['redirect_uri'] :
3503 $this->router->pathFor('slash');
3504
3505 if (!isset($post['confirm'])) {
3506 $this->flash->addMessage(
3507 'error_detected',
3508 _T("Removal has not been confirmed!")
3509 );
3510 } else {
3511 if (isset($this->session->filter_savedsearch)) {
3512 $filters = $this->session->filter_savedsearch;
3513 } else {
3514 $filters = new SavedSearchesList();
3515 }
3516 $searches = new SavedSearches($this->zdb, $this->login, $filters);
3517
3518 if (!is_array($post['id'])) {
3519 $ids = (array)$post['id'];
3520 } else {
3521 $ids = $post['id'];
3522 }
3523
3524 $del = $searches->remove($ids, $this->history);
3525
3526 if ($del !== true) {
3527 $error_detected = _T("An error occurred trying to remove searches :/");
3528
3529 $this->flash->addMessage(
3530 'error_detected',
3531 $error_detected
3532 );
3533 } else {
3534 $success_detected = str_replace(
3535 '%count',
3536 count($ids),
3537 _T("%count searches have been successfully deleted.")
3538 );
3539
3540 $this->flash->addMessage(
3541 'success_detected',
3542 $success_detected
3543 );
3544
3545 $success = true;
3546 }
3547 }
3548
3549 if (!$ajax) {
3550 return $response
3551 ->withStatus(301)
3552 ->withHeader('Location', $uri);
3553 } else {
3554 return $response->withJson(
3555 [
3556 'success' => $success
3557 ]
3558 );
3559 }
3560 }
3561 )->setName('doRemoveSearch')->add($authenticate);
3562
3563 $app->get(
3564 '/save-search/{id}',
3565 function ($request, $response, $args) {
3566 try {
3567 $sco = new Galette\Entity\SavedSearch($this->zdb, $this->login, (int)$args['id']);
3568 $this->flash->addMessage(
3569 'success_detected',
3570 _T("Saved search loaded")
3571 );
3572 } catch (\Exception $e) {
3573 $this->flash->addMessage(
3574 'error_detected',
3575 _T("An SQL error has occurred while storing search.")
3576 );
3577 }
3578 $parameters = (array)$sco->parameters;
3579
3580 $filters = null;
3581 if (isset($parameters['free_search'])) {
3582 $filters = new AdvancedMembersList();
3583 } else {
3584 $filters = new MembersList();
3585 }
3586
3587 foreach ($parameters as $key => $value) {
3588 $filters->$key = $value;
3589 }
3590 $this->session->filter_members = $filters;
3591
3592 return $response
3593 ->withStatus(301)
3594 ->withHeader('Location', $this->router->pathFor('members'));
3595 }
3596 )->setName('loadSearch');