]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Repository/Members.php
84a65805b8b993c0482741cd133699c077ac3410
[galette.git] / galette / lib / Galette / Repository / Members.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Members class
7 *
8 * PHP version 5
9 *
10 * Copyright © 2009-2014 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 Repository
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2009-2014 The Galette Team
32 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
33 * @link http://galette.tuxfamily.org
34 * @since Available since 0.7dev - 2009-02-28
35 */
36
37 namespace Galette\Repository;
38
39 use Galette\DynamicFields\DynamicField;
40 use Galette\Entity\DynamicFieldsHandle;
41 use Analog\Analog;
42 use Laminas\Db\Adapter\Adapter;
43 use Laminas\Db\Sql\Expression;
44 use Laminas\Db\Sql\Select;
45 use Laminas\Db\Sql\Predicate\PredicateSet;
46 use Laminas\Db\Sql\Predicate\Operator;
47 use Galette\Entity\Adherent;
48 use Galette\Entity\Contribution;
49 use Galette\Entity\Transaction;
50 use Galette\Entity\Reminder;
51 use Galette\Filters\MembersList;
52 use Galette\Filters\AdvancedMembersList;
53 use Galette\Core\Picture;
54 use Galette\Entity\Group;
55 use Galette\Repository\Groups;
56 use Galette\Entity\Status;
57 use Galette\Core\Db;
58
59 /**
60 * Members class for galette
61 *
62 * @name Members
63 * @category Repository
64 * @package Galette
65 *
66 * @author Johan Cwiklinski <johan@x-tnd.be>
67 * @copyright 2009-2014 The Galette Team
68 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
69 * @link http://galette.tuxfamily.org
70 */
71 class Members
72 {
73 public const TABLE = Adherent::TABLE;
74 public const PK = Adherent::PK;
75
76 public const ALL_ACCOUNTS = 0;
77 public const ACTIVE_ACCOUNT = 1;
78 public const INACTIVE_ACCOUNT = 2;
79
80 public const SHOW_LIST = 0;
81 public const SHOW_PUBLIC_LIST = 1;
82 public const SHOW_ARRAY_LIST = 2;
83 public const SHOW_STAFF = 3;
84 public const SHOW_MANAGED = 4;
85 public const SHOW_EXPORT = 5;
86
87 public const FILTER_NAME = 0;
88 public const FILTER_ADDRESS = 1;
89 public const FILTER_MAIL = 2;
90 public const FILTER_JOB = 3;
91 public const FILTER_INFOS = 4;
92 public const FILTER_DC_EMAIL = 5;
93 public const FILTER_W_EMAIL = 6;
94 public const FILTER_WO_EMAIL = 7;
95 public const FILTER_COMPANY_NAME = 8;
96 public const FILTER_DC_PUBINFOS = 9;
97 public const FILTER_W_PUBINFOS = 10;
98 public const FILTER_WO_PUBINFOS = 11;
99 public const FILTER_NUMBER = 12;
100
101 public const MEMBERSHIP_ALL = 0;
102 public const MEMBERSHIP_UP2DATE = 3;
103 public const MEMBERSHIP_NEARLY = 1;
104 public const MEMBERSHIP_LATE = 2;
105 public const MEMBERSHIP_NEVER = 4;
106 public const MEMBERSHIP_STAFF = 5;
107 public const MEMBERSHIP_ADMIN = 6;
108 public const MEMBERSHIP_NONE = 7;
109
110 public const ORDERBY_NAME = 0;
111 public const ORDERBY_NICKNAME = 1;
112 public const ORDERBY_STATUS = 2;
113 public const ORDERBY_FEE_STATUS = 3;
114 public const ORDERBY_MODIFDATE = 4;
115 public const ORDERBY_ID = 5;
116
117 public const NON_STAFF_MEMBERS = 30;
118
119 private $filters = false;
120 private $count = null;
121 private $errors = array();
122
123 /**
124 * Default constructor
125 *
126 * @param MembersList $filters Filtering
127 */
128 public function __construct($filters = null)
129 {
130 if ($filters === null) {
131 $this->filters = new MembersList();
132 } else {
133 $this->filters = $filters;
134 }
135 }
136
137 /**
138 * Get staff members list
139 *
140 * @param bool $as_members return the results as an array of
141 * Member object.
142 * @param array $fields field(s) name(s) to get. Should be a string or
143 * an array. If null, all fields will be
144 * returned
145 * @param boolean $count true if we want to count members
146 * @param boolean $limit true to LIMIT query
147 *
148 * @return Adherent[]|ResultSet
149 */
150 public function getStaffMembersList(
151 $as_members = false,
152 $fields = null,
153 $count = true,
154 $limit = true
155 ) {
156 return $this->getMembersList(
157 $as_members,
158 $fields,
159 $count,
160 true,
161 false,
162 $limit
163 );
164 }
165
166 /**
167 * Get managed members list (for groups managers)
168 *
169 * @param bool $as_members return the results as an array of
170 * Member object.
171 * @param array $fields field(s) name(s) to get. Should be a string or
172 * an array. If null, all fields will be
173 * returned
174 * @param boolean $count true if we want to count members
175 * @param boolean $limit true to LIMIT query
176 *
177 * @return Adherent[]|ResultSet
178 */
179 public function getManagedMembersList(
180 $as_members = false,
181 $fields = null,
182 $count = true,
183 $limit = true
184 ) {
185 return $this->getMembersList(
186 $as_members,
187 $fields,
188 $count,
189 false,
190 true,
191 $limit
192 );
193 }
194
195 /**
196 * Get members list
197 *
198 * @param bool $as_members return the results as an array of
199 * Member object.
200 * @param array $fields field(s) name(s) to get. Should be a string or
201 * an array. If null, all fields will be
202 * returned
203 * @param boolean $count true if we want to count members
204 * @param boolean $staff true if we want only staff members
205 * @param boolean $managed true if we want only managed groups
206 * @param boolean $limit true if we want records pagination
207 * @param boolean $export true if we are exporting
208 *
209 * @return Adherent[]|Laminas\Db\ResultSet
210 */
211 public function getMembersList(
212 $as_members = false,
213 $fields = null,
214 $count = true,
215 $staff = false,
216 $managed = false,
217 $limit = true,
218 $export = false
219 ) {
220 global $zdb;
221
222 if ($limit === true) {
223 //force count if limit is active
224 $count = true;
225 }
226
227 try {
228 $_mode = self::SHOW_LIST;
229 if ($staff !== false) {
230 $_mode = self::SHOW_STAFF;
231 }
232 if ($managed !== false) {
233 $_mode = self::SHOW_MANAGED;
234 }
235 if ($export !== false) {
236 $_mode = self::SHOW_EXPORT;
237 }
238
239 $select = $this->buildSelect(
240 $_mode,
241 $fields,
242 false,
243 $count
244 );
245
246 //add limits to retrieve only relavant rows
247 if ($limit === true) {
248 $this->filters->setLimits($select);
249 }
250
251 $rows = $zdb->execute($select);
252 $this->filters->query = $zdb->query_string;
253
254 $members = array();
255 if ($as_members) {
256 $deps = array(
257 'picture' => false,
258 'groups' => false
259 );
260 foreach ($rows as $row) {
261 $members[] = new Adherent($zdb, $row, $deps);
262 }
263 } else {
264 $members = $rows;
265 }
266 return $members;
267 } catch (\Exception $e) {
268 Analog::log(
269 'Cannot list members | ' . $e->getMessage(),
270 Analog::WARNING
271 );
272 throw $e;
273 }
274 }
275
276 /**
277 * Remove specified members
278 *
279 * @param integer|array $ids Members identifiers to delete
280 *
281 * @return boolean
282 */
283 public function removeMembers($ids)
284 {
285 global $zdb, $hist, $emitter;
286
287 $list = array();
288 if (is_numeric($ids)) {
289 //we've got only one identifier
290 $list[] = $ids;
291 } else {
292 $list = $ids;
293 }
294
295 if (is_array($list)) {
296 try {
297 $zdb->connection->beginTransaction();
298
299 //Retrieve some information
300 $select = $zdb->select(self::TABLE);
301 $select->columns(
302 array(self::PK, 'nom_adh', 'prenom_adh', 'email_adh')
303 )->where->in(self::PK, $list);
304
305 $results = $zdb->execute($select);
306
307 $infos = null;
308 foreach ($results as $member) {
309 $str_adh = $member->id_adh . ' (' . $member->nom_adh . ' ' .
310 $member->prenom_adh . ')';
311 $infos .= $str_adh . "\n";
312
313 $p = new Picture($member->id_adh);
314 if ($p->hasPicture()) {
315 if (!$p->delete(false)) {
316 Analog::log(
317 'Unable to delete picture for member ' . $str_adh,
318 Analog::ERROR
319 );
320 throw new \Exception(
321 'Unable to delete picture for member ' .
322 $str_adh
323 );
324 } else {
325 $hist->add(
326 _T("Member Picture deleted"),
327 $str_adh
328 );
329 }
330 }
331
332 $emitter->emit('member.remove', [
333 'id_adh' => $member->id_adh,
334 'nom_adh' => $member->nom_adh,
335 'prenom_adh' => $member->prenom_adh,
336 'email_adh' => $member->email_adh
337 ]);
338 }
339
340 //delete contributions
341 $del_qry = $zdb->delete(Contribution::TABLE);
342 $del_qry->where->in(
343 self::PK,
344 $list
345 );
346 $zdb->execute($del_qry);
347
348 //get transactions
349 $select = $zdb->select(Transaction::TABLE);
350 $select->where->in(self::PK, $list);
351 $results = $zdb->execute($select);
352
353 //if members has transactions;
354 //reset link with other contributions
355 //and remove them
356 if ($results->count() > 0) {
357 $transactions = [];
358 foreach ($results as $transaction) {
359 $transactions[] = $transaction[Transaction::PK];
360 }
361
362 $update = $zdb->update(Contribution::TABLE);
363 $update->set([
364 Transaction::PK => new Expression('NULL')
365 ])->where->in(
366 Transaction::PK,
367 $transactions
368 );
369 $zdb->execute($update);
370 }
371
372 //delete transactions
373 $del_qry = $zdb->delete(Transaction::TABLE);
374 $del_qry->where->in(self::PK, $list);
375 $zdb->execute($del_qry);
376
377 //delete groups membership/mamagmentship
378 Groups::removeMembersFromGroups($list);
379
380 //delete reminders
381 $del_qry = $zdb->delete(Reminder::TABLE);
382 $del_qry->where->in(
383 'reminder_dest',
384 $list
385 );
386 $zdb->execute($del_qry);
387
388 //delete dynamic fields values
389 $del_qry = $zdb->delete(DynamicFieldsHandle::TABLE);
390 $del_qry->where(['field_form' => 'adh']);
391 $del_qry->where->in('item_id', $list);
392 $zdb->execute($del_qry);
393
394 //delete members
395 $del_qry = $zdb->delete(self::TABLE);
396 $del_qry->where->in(
397 self::PK,
398 $list
399 );
400 $zdb->execute($del_qry);
401
402 //commit all changes
403 $zdb->connection->commit();
404
405 //add an history entry
406 $hist->add(
407 _T("Delete members cards, transactions and dues"),
408 $infos
409 );
410
411 return true;
412 } catch (\Exception $e) {
413 $zdb->connection->rollBack();
414 if ($e->getCode() == 23000) {
415 Analog::log(
416 'Member still have existing dependencies in the ' .
417 'database, maybe a mailing or some content from a ' .
418 'plugin. Please remove dependencies before trying ' .
419 'to remove him.',
420 Analog::ERROR
421 );
422 $this->errors[] = _T("Cannot remove a member who still have dependencies (mailings, ...)");
423 } else {
424 Analog::log(
425 'Unable to delete selected member(s) |' .
426 $e->getMessage(),
427 Analog::ERROR
428 );
429 }
430 return false;
431 }
432 } else {
433 //not numeric and not an array: incorrect.
434 Analog::log(
435 'Asking to remove members, but without providing an array or a single numeric value.',
436 Analog::ERROR
437 );
438 return false;
439 }
440 }
441
442 /**
443 * Get members list
444 *
445 * @param boolean $as_members return the results as an array of
446 * Member object.
447 * @param array $fields field(s) name(s) to get. Should be a string or
448 * an array. If null, all fields will be
449 * returned
450 *
451 * @return Adherent[]|ResultSet
452 */
453 public function getList($as_members = false, $fields = null)
454 {
455 return $this->getMembersList(
456 $as_members,
457 $fields,
458 false,
459 false,
460 false,
461 true,
462 false
463 );
464 }
465
466 /**
467 * Get members list with public information available
468 *
469 * @param boolean $with_photos get only members which have uploaded a
470 * photo (for trombinoscope)
471 *
472 * @return Adherent[]
473 */
474 public function getPublicList($with_photos)
475 {
476 global $zdb;
477
478 try {
479 $select = $this->buildSelect(
480 self::SHOW_PUBLIC_LIST,
481 null,
482 $with_photos
483 );
484
485 $this->filters->setLimits($select);
486
487 $results = $zdb->execute($select);
488 $members = array();
489 foreach ($results as $row) {
490 $deps = array(
491 'groups' => false,
492 'dues' => false,
493 'picture' => $with_photos
494 );
495 $members[] = new Adherent($zdb, $row, $deps);
496 }
497 return $members;
498 } catch (\Exception $e) {
499 Analog::log(
500 'Cannot list members with public information (photos: '
501 . $with_photos . ') | ' . $e->getMessage(),
502 Analog::WARNING
503 );
504 return false;
505 }
506 }
507
508 /**
509 * Get list of members that has been selected
510 *
511 * @param array $ids an array of members id that has been selected
512 * @param array $orderby SQL order clause (optionnal)
513 * @param boolean $with_photos Should photos be loaded?
514 * @param boolean $as_members Return Adherent[] or simple ResultSet
515 * @param array $fields Fields to use
516 * @param boolean $export True if we are exporting
517 * @param boolean $dues True if load dues as Adherent dependency
518 * @param boolean $parent True if load parent as Adherent dependency
519 *
520 * @return Adherent[]
521 */
522 public function getArrayList(
523 $ids,
524 $orderby = null,
525 $with_photos = false,
526 $as_members = true,
527 $fields = null,
528 $export = false,
529 $dues = false,
530 $parent = false
531 ) {
532 global $zdb;
533
534 if (!is_array($ids) || count($ids) < 1) {
535 Analog::log('No member selected for labels.', Analog::INFO);
536 return false;
537 }
538
539 try {
540 $damode = self::SHOW_ARRAY_LIST;
541 if ($export === true) {
542 $damode = self::SHOW_EXPORT;
543 }
544 $select = $this->buildSelect(
545 $damode,
546 $fields,
547 false,
548 false
549 );
550 $select->where->in('a.' . self::PK, $ids);
551 if ($orderby != null && count($orderby) > 0) {
552 if (is_array($orderby)) {
553 foreach ($orderby as $o) {
554 $select->order($o);
555 }
556 } else {
557 $select->order($orderby);
558 }
559 }
560
561 $results = $zdb->execute($select);
562
563 $members = array();
564 foreach ($results as $o) {
565 $deps = array(
566 'picture' => $with_photos,
567 'groups' => false,
568 'dues' => $dues,
569 'parent' => $parent
570 );
571 if ($as_members === true) {
572 $members[] = new Adherent($zdb, $o, $deps);
573 } else {
574 $members[] = $o;
575 }
576 }
577 return $members;
578 } catch (\Exception $e) {
579 Analog::log(
580 'Cannot load members form ids array | ' . $e->getMessage(),
581 Analog::WARNING
582 );
583 }
584 }
585
586 /**
587 * Builds the SELECT statement
588 *
589 * @param int $mode the current mode (see self::SHOW_*)
590 * @param array $fields fields list to retrieve
591 * @param bool $photos true if we want to get only members with photos
592 * Default to false, only relevant for SHOW_PUBLIC_LIST
593 * @param bool $count true if we want to count members, defaults to false
594 *
595 * @return Select SELECT statement
596 */
597 private function buildSelect($mode, $fields, $photos, $count = false): Select
598 {
599 global $zdb, $login;
600
601 try {
602 if ($fields != null && is_array($fields) && !in_array('id_adh', $fields)) {
603 $fields[] = 'id_adh';
604 }
605 $fieldsList = ($fields != null)
606 ? ((!is_array($fields) || count($fields) < 1) ? (array)'*'
607 : $fields) : (array)'*';
608
609 $select = $zdb->select(self::TABLE, 'a');
610
611 $select->columns($fieldsList);
612
613 $select->quantifier('DISTINCT');
614
615 switch ($mode) {
616 case self::SHOW_STAFF:
617 case self::SHOW_LIST:
618 case self::SHOW_ARRAY_LIST:
619 $select->join(
620 array('p' => PREFIX_DB . Status::TABLE),
621 'a.' . Status::PK . '=p.' . Status::PK,
622 array()
623 );
624 break;
625 case self::SHOW_EXPORT:
626 //basically the same as above, but without any fields
627 $select->join(
628 array('p' => PREFIX_DB . Status::TABLE),
629 'a.' . Status::PK . '=p.' . Status::PK,
630 array()
631 );
632 break;
633 case self::SHOW_MANAGED:
634 $select->join(
635 array('p' => PREFIX_DB . Status::TABLE),
636 'a.' . Status::PK . '=p.' . Status::PK
637 )->join(
638 array('gr' => PREFIX_DB . Group::GROUPSUSERS_TABLE),
639 'a.' . Adherent::PK . '=gr.' . Adherent::PK,
640 array()
641 )->join(
642 array('m' => PREFIX_DB . Group::GROUPSMANAGERS_TABLE),
643 'gr.' . Group::PK . '=m.' . Group::PK,
644 array()
645 )->where('m.' . Adherent::PK . ' = ' . $login->id);
646 break;
647 case self::SHOW_PUBLIC_LIST:
648 if ($photos) {
649 $select->join(
650 array('p' => PREFIX_DB . Picture::TABLE),
651 'a.' . self::PK . '= p.' . self::PK,
652 array()
653 );
654 }
655 break;
656 }
657
658 //check for contributions filtering
659 if (
660 $this->filters instanceof AdvancedMembersList
661 && $this->filters->withinContributions()
662 ) {
663 $select->join(
664 array('ct' => PREFIX_DB . Contribution::TABLE),
665 'ct.' . self::PK . '=a.' . self::PK,
666 array(),
667 $select::JOIN_LEFT
668 );
669 }
670
671 //check if there are dynamic fields in filter
672 $hasDf = false;
673 $dfs = array();
674
675 if (
676 $this->filters instanceof AdvancedMembersList
677 && $this->filters->free_search
678 && count($this->filters->free_search) > 0
679 && !isset($this->filters->free_search['empty'])
680 ) {
681 $free_searches = $this->filters->free_search;
682 foreach ($free_searches as $fs) {
683 if (strpos($fs['field'], 'dyn_') === 0) {
684 // simple dynamic fields
685 $hasDf = true;
686 $dfs[] = str_replace('dyn_', '', $fs['field']);
687 }
688 }
689 }
690
691 //check if there are dynamic fields for contributions in filter
692 $hasDfc = false;
693 $hasCdfc = false;
694 $cdfcs = array();
695
696 if (
697 $this->filters instanceof AdvancedMembersList
698 && $this->filters->withinContributions()
699 ) {
700 if (
701 $this->filters->contrib_dynamic
702 && count($this->filters->contrib_dynamic) > 0
703 && !isset($this->filters->contrib_dynamic['empty'])
704 ) {
705 $hasDfc = true;
706
707 //check if there are dynamic fields in the filter
708 foreach ($this->filters->contrib_dynamic as $k => $cd) {
709 if (is_array($cd)) {
710 $hasCdfc = true;
711 $cdfcs[] = $k;
712 }
713 }
714 }
715 }
716
717 if ($hasDfc === true || $hasCdfc === true) {
718 $select->join(
719 array('dfc' => PREFIX_DB . DynamicFieldsHandle::TABLE),
720 'dfc.item_id=ct.' . Contribution::PK,
721 array(),
722 $select::JOIN_LEFT
723 );
724 }
725
726 // simple dynamic fields
727 if ($hasDf === true) {
728 foreach ($dfs as $df) {
729 $subselect = $zdb->select(DynamicFieldsHandle::TABLE, 'df');
730 $subselect->columns(
731 [
732 'item_id' => 'item_id',
733 'val' => 'field_val'
734 ]
735 );
736 $subselect->where('df.field_form = \'adh\'');
737 $subselect->where('df.field_id = ' . $df);
738 $select->join(
739 array('df' . $df => $subselect),
740 'a.id_adh = df' . $df . '.item_id',
741 array(),
742 $select::JOIN_LEFT
743 );
744 }
745 }
746
747 // choice dynamic fields
748 if ($hasCdfc === true) {
749 $cdf_field = 'cdf.id';
750 if (TYPE_DB === 'pgsql') {
751 $cdf_field .= '::text';
752 }
753
754 $cdf_field = 'cdfc.id';
755 if (TYPE_DB === 'pgsql') {
756 $cdf_field .= '::text';
757 }
758 foreach ($cdfcs as $cdf) {
759 $rcdf_field = str_replace(
760 'cdfc.',
761 'cdfc' . $cdf . '.',
762 $cdf_field
763 );
764 $select->join(
765 array('cdfc' . $cdf => DynamicField::getFixedValuesTableName($cdf, true)),
766 $rcdf_field . '=dfc.field_val',
767 array(),
768 $select::JOIN_LEFT
769 );
770 }
771 }
772
773 if ($mode == self::SHOW_LIST || $mode == self::SHOW_MANAGED) {
774 if ($this->filters !== false) {
775 $this->buildWhereClause($select);
776 }
777 } elseif ($mode == self::SHOW_PUBLIC_LIST) {
778 $select->where(
779 array(
780 new PredicateSet(
781 array(
782 new Operator(
783 'date_echeance',
784 '>=',
785 date('Y-m-d')
786 ),
787 new Operator(
788 'bool_exempt_adh',
789 '=',
790 new Expression('true')
791 )
792 ),
793 PredicateSet::OP_OR
794 ),
795 new PredicateSet(
796 array(
797 new Operator(
798 'bool_display_info',
799 '=',
800 new Expression('true')
801 ),
802 new Operator(
803 'activite_adh',
804 '=',
805 new Expression('true')
806 )
807 ),
808 PredicateSet::OP_AND
809 )
810 )
811 );
812 }
813
814 if ($mode === self::SHOW_STAFF) {
815 $select->where->lessThan(
816 'p.priorite_statut',
817 self::NON_STAFF_MEMBERS
818 );
819 }
820
821 if ($count) {
822 $this->proceedCount($select);
823 }
824
825 $this->buildOrderClause($select, $fields);
826
827 return $select;
828 } catch (\Exception $e) {
829 Analog::log(
830 'Cannot build SELECT clause for members | ' . $e->getMessage(),
831 Analog::WARNING
832 );
833 throw $e;
834 }
835 }
836
837 /**
838 * Count members from the query
839 *
840 * @param Select $select Original select
841 *
842 * @return void
843 */
844 private function proceedCount(Select $select)
845 {
846 global $zdb;
847
848 try {
849 $countSelect = clone $select;
850 $countSelect->reset($countSelect::COLUMNS);
851 $countSelect->reset($countSelect::ORDER);
852 $countSelect->reset($countSelect::HAVING);
853 $joins = $countSelect->joins;
854 $countSelect->reset($countSelect::JOINS);
855 foreach ($joins as $join) {
856 $countSelect->join(
857 $join['name'],
858 $join['on'],
859 [],
860 $join['type']
861 );
862 unset($join['columns']);
863 }
864 $countSelect->columns(
865 array(
866 'count' => new Expression('count(DISTINCT a.' . self::PK . ')')
867 )
868 );
869
870 $have = $select->having;
871 if ($have->count() > 0) {
872 foreach ($have->getPredicates() as $h) {
873 $countSelect->where($h);
874 }
875 }
876
877 $results = $zdb->execute($countSelect);
878
879 $this->count = $results->current()->count;
880 if (isset($this->filters) && $this->count > 0) {
881 $this->filters->setCounter($this->count);
882 }
883 } catch (\Exception $e) {
884 Analog::log(
885 'Cannot count members | ' . $e->getMessage(),
886 Analog::WARNING
887 );
888 return false;
889 }
890 }
891
892 /**
893 * Builds the order clause
894 *
895 * @param Select $select Original select
896 * @param array $fields Fields list to ensure ORDER clause
897 * references selected fields. Optionnal.
898 *
899 * @return Select
900 */
901 private function buildOrderClause(Select $select, $fields = null): Select
902 {
903 $order = array();
904
905 switch ($this->filters->orderby) {
906 case self::ORDERBY_NICKNAME:
907 if ($this->canOrderBy('pseudo_adh', $fields)) {
908 $order[] = 'pseudo_adh ' . $this->filters->getDirection();
909 }
910 break;
911 case self::ORDERBY_STATUS:
912 if ($this->canOrderBy('priorite_statut', $fields)) {
913 $order[] = 'priorite_statut ' . $this->filters->getDirection();
914 }
915 break;
916 case self::ORDERBY_MODIFDATE:
917 if ($this->canOrderBy('date_modif_adh', $fields)) {
918 $order[] = 'date_modif_adh ' . $this->filters->getDirection();
919 }
920 break;
921 case 'list_adh_contribstatus':
922 case self::ORDERBY_FEE_STATUS:
923 if ($this->canOrderBy('bool_exempt_adh', $fields)) {
924 $order[] = 'bool_exempt_adh ' . $this->filters->getDirection();
925 }
926
927 if ($this->canOrderBy('date_echeance', $fields)) {
928 $order[] = 'date_echeance ' . $this->filters->getDirection();
929 }
930 break;
931 case self::ORDERBY_ID:
932 if ($this->canOrderBy('id_adh', $fields)) {
933 $order[] = 'id_adh ' . $this->filters->getDirection();
934 }
935 break;
936 case 'list_adh_name':
937 case 'nom_adh':
938 case 'prenom_adh':
939 //defaults
940 break;
941 default:
942 if ($this->canOrderBy($this->filters->orderby, $fields)) {
943 $order[] = $this->filters->orderby . ' ' . $this->filters->getDirection();
944 }
945 break;
946 }
947
948 //anyways, we want to order by firstname, lastname
949 if ($this->canOrderBy('nom_adh', $fields)) {
950 $order[] = 'nom_adh ' . $this->filters->getDirection();
951 }
952 if ($this->canOrderBy('prenom_adh', $fields)) {
953 $order[] = 'prenom_adh ' . $this->filters->getDirection();
954 }
955
956 $select->order($order);
957 return $select;
958 }
959
960 /**
961 * Is field allowed to order? it shoulsd be present in
962 * provided fields list (those that are SELECT'ed).
963 *
964 * @param string $field_name Field name to order by
965 * @param array $fields SELECTE'ed fields
966 *
967 * @return boolean
968 */
969 private function canOrderBy($field_name, $fields)
970 {
971 if ($fields === null) {
972 return true;
973 } elseif (!is_array($fields)) {
974 return false;
975 } elseif (in_array($field_name, $fields)) {
976 return true;
977 } else {
978 Analog::log(
979 'Trying to order by ' . $field_name . ' while it is not in ' .
980 'selected fields.',
981 Analog::WARNING
982 );
983 return false;
984 }
985 }
986
987 /**
988 * Builds where clause, for filtering on simple list mode
989 *
990 * @param Select $select Original select
991 *
992 * @return void
993 */
994 private function buildWhereClause(Select $select)
995 {
996 global $zdb, $login;
997
998 try {
999 if ($this->filters->email_filter == self::FILTER_W_EMAIL) {
1000 $select->where('email_adh != \'\'');
1001 }
1002 if ($this->filters->email_filter == self::FILTER_WO_EMAIL) {
1003 $select->where('(email_adh = \'\' OR email_adh IS NULL)');
1004 }
1005
1006 if ($this->filters->filter_str != '') {
1007 $token = $zdb->platform->quoteValue(
1008 '%' . strtolower($this->filters->filter_str) . '%'
1009 );
1010 switch ($this->filters->field_filter) {
1011 case self::FILTER_NAME:
1012 if (TYPE_DB === 'pgsql') {
1013 $sep = " || ' ' || ";
1014 $pre = '';
1015 $post = '';
1016 } else {
1017 $sep = ', " ", ';
1018 $pre = 'CONCAT(';
1019 $post = ')';
1020 }
1021
1022 $select->where(
1023 '(' .
1024 $pre . 'LOWER(nom_adh)' . $sep .
1025 'LOWER(prenom_adh)' . $sep .
1026 'LOWER(pseudo_adh)' . $post . ' LIKE ' .
1027 $token
1028 . ' OR ' .
1029 $pre . 'LOWER(prenom_adh)' . $sep .
1030 'LOWER(nom_adh)' . $sep .
1031 'LOWER(pseudo_adh)' . $post . ' LIKE ' .
1032 $token
1033 . ')'
1034 );
1035 break;
1036 case self::FILTER_COMPANY_NAME:
1037 $select->where(
1038 'LOWER(societe_adh) LIKE ' .
1039 $token
1040 );
1041 break;
1042 case self::FILTER_ADDRESS:
1043 $select->where(
1044 '(' .
1045 'LOWER(adresse_adh) LIKE ' . $token
1046 . ' OR ' .
1047 'LOWER(adresse2_adh) LIKE ' . $token
1048 . ' OR ' .
1049 'cp_adh LIKE ' . $token
1050 . ' OR ' .
1051 'LOWER(ville_adh) LIKE ' . $token
1052 . ' OR ' .
1053 'LOWER(pays_adh) LIKE ' . $token
1054 . ')'
1055 );
1056 break;
1057 case self::FILTER_MAIL:
1058 $select->where(
1059 '(' .
1060 'LOWER(email_adh) LIKE ' . $token
1061 . ' OR ' .
1062 'LOWER(url_adh) LIKE ' . $token
1063 . ' OR ' .
1064 'LOWER(msn_adh) LIKE ' . $token
1065 . ' OR ' .
1066 'LOWER(icq_adh) LIKE ' . $token
1067 . ' OR ' .
1068 'LOWER(jabber_adh) LIKE ' . $token
1069 . ')'
1070 );
1071 break;
1072 case self::FILTER_JOB:
1073 $select->where(
1074 'LOWER(prof_adh) LIKE ' . $token
1075 );
1076 break;
1077 case self::FILTER_INFOS:
1078 $more = '';
1079 if ($login->isAdmin() || $login->isStaff()) {
1080 $more = ' OR LOWER(info_adh) LIKE ' . $token;
1081 }
1082 $select->where(
1083 '(LOWER(info_public_adh) LIKE ' .
1084 $token . $more . ')'
1085 );
1086 break;
1087 case self::FILTER_NUMBER:
1088 $select->where->equalTo('a.id_adh', $this->filters->filter_str);
1089 break;
1090 }
1091 }
1092
1093 if ($this->filters->membership_filter) {
1094 switch ($this->filters->membership_filter) {
1095 case self::MEMBERSHIP_NEARLY:
1096 $now = new \DateTime();
1097 $duedate = new \DateTime();
1098 $duedate->modify('+1 month');
1099 $select->where->greaterThanOrEqualTo(
1100 'date_echeance',
1101 $now->format('Y-m-d')
1102 )->lessThan(
1103 'date_echeance',
1104 $duedate->format('Y-m-d')
1105 );
1106 break;
1107 case self::MEMBERSHIP_LATE:
1108 $select->where
1109 ->lessThan(
1110 'date_echeance',
1111 date('Y-m-d', time())
1112 )->equalTo('bool_exempt_adh', new Expression('false'));
1113 break;
1114 case self::MEMBERSHIP_UP2DATE:
1115 $select->where(
1116 '(' . 'date_echeance >= \'' . date('Y-m-d', time())
1117 . '\' OR bool_exempt_adh=true)'
1118 );
1119 break;
1120 case self::MEMBERSHIP_NEVER:
1121 $select->where('date_echeance IS NULL')
1122 ->where('bool_exempt_adh = false');
1123 break;
1124 case self::MEMBERSHIP_STAFF:
1125 $select->where->lessThan(
1126 'p.priorite_statut',
1127 self::NON_STAFF_MEMBERS
1128 );
1129 break;
1130 case self::MEMBERSHIP_ADMIN:
1131 $select->where->equalTo('bool_admin_adh', true);
1132 break;
1133 case self::MEMBERSHIP_NONE:
1134 $select->where->equalTo('a.id_statut', Status::DEFAULT_STATUS);
1135 break;
1136 }
1137 }
1138
1139 if ($this->filters->filter_account) {
1140 switch ($this->filters->filter_account) {
1141 case self::ACTIVE_ACCOUNT:
1142 $select->where('activite_adh=true');
1143 break;
1144 case self::INACTIVE_ACCOUNT:
1145 $select->where('activite_adh=false');
1146 break;
1147 }
1148 }
1149
1150 if ($this->filters->group_filter) {
1151 $select->join(
1152 array('g' => PREFIX_DB . Group::GROUPSUSERS_TABLE),
1153 'a.' . Adherent::PK . '=g.' . Adherent::PK,
1154 array(),
1155 $select::JOIN_LEFT
1156 )->join(
1157 array('gs' => PREFIX_DB . Group::TABLE),
1158 'gs.' . Group::PK . '=g.' . Group::PK,
1159 array(),
1160 $select::JOIN_LEFT
1161 )->where(
1162 '(g.' . Group::PK . ' = ' . $this->filters->group_filter .
1163 ' OR gs.parent_group = NULL OR gs.parent_group = ' .
1164 $this->filters->group_filter . ')'
1165 );
1166 }
1167
1168 if ($this->filters instanceof AdvancedMembersList) {
1169 $this->buildAdvancedWhereClause($select);
1170 }
1171
1172 return $select;
1173 } catch (\Exception $e) {
1174 Analog::log(
1175 __METHOD__ . ' | ' . $e->getMessage(),
1176 Analog::WARNING
1177 );
1178 throw $e;
1179 }
1180 }
1181
1182 /**
1183 * Builds where clause, for advanced filtering on simple list mode
1184 *
1185 * @param Select $select Original select
1186 *
1187 * @return void
1188 */
1189 private function buildAdvancedWhereClause(Select $select)
1190 {
1191 global $zdb, $login;
1192
1193 // Search members who belong to any (OR) or all (AND) listed groups.
1194 // Idea is to build an array of members ID that fits groups selection
1195 // we will use in the final query.
1196 // The OR case is quite simple, AND is a bit more complex; since we must
1197 // check each member do belongs to all listed groups.
1198 if (
1199 count($this->filters->groups_search) > 0
1200 && !isset($this->filters->groups_search['empty'])
1201 ) {
1202 $groups_adh = [];
1203 $wheregroups = [];
1204
1205 foreach ($this->filters->groups_search as $gs) { // then add a row for each group
1206 $wheregroups[] = $gs['group'];
1207 }
1208
1209 $gselect = $zdb->select(Group::GROUPSUSERS_TABLE, 'gu');
1210 $gselect->columns(
1211 array('id_adh')
1212 )->join(
1213 array('g' => PREFIX_DB . Group::TABLE),
1214 'gu.id_group=g.' . Group::PK,
1215 array(),
1216 $select::JOIN_LEFT
1217 )->where(
1218 array(
1219 'g.id_group' => ':group',
1220 'g.parent_group' => ':pgroup'
1221 ),
1222 PredicateSet::OP_OR
1223 );
1224 $gselect->group(['gu.id_adh']);
1225
1226 $stmt = $zdb->sql->prepareStatementForSqlObject($gselect);
1227
1228 $mids = [];
1229 $ids = [];
1230 foreach ($this->filters->groups_search as $gs) { // then add a row for each ig/searched group pair
1231 /** Why where parameter is named where1 ?? */
1232 $gresults = $stmt->execute(
1233 array(
1234 'where1' => $gs['group'],
1235 'where2' => $gs['group']
1236 )
1237 );
1238
1239 switch ($this->filters->groups_search_log_op) {
1240 case AdvancedMembersList::OP_AND:
1241 foreach ($gresults as $gresult) {
1242 if (!isset($ids[$gresult['id_adh']])) {
1243 $ids[$gresult['id_adh']] = 0;
1244 }
1245 $ids[$gresult['id_adh']] += 1;
1246 }
1247 break;
1248 case AdvancedMembersList::OP_OR:
1249 foreach ($gresults as $gresult) {
1250 $mids[$gresult['id_adh']] = $gresult['id_adh'];
1251 }
1252 break;
1253 }
1254 }
1255
1256 if (count($ids)) {
1257 foreach ($ids as $id_adh => $count) {
1258 if ($count == count($wheregroups)) {
1259 $mids[$id_adh] = $id_adh;
1260 }
1261 }
1262 }
1263
1264 if (count($mids)) {
1265 //limit on found members
1266 $select->where->in('a.id_adh', $mids);
1267 } else {
1268 //no match in groups, end of game.
1269 $select->where('false = true');
1270 }
1271 }
1272
1273 //shoudl be retrieved from members_fields
1274 $dates = [
1275 'ddn_adh' => 'birth_date',
1276 'date_crea_adh' => 'creation_date',
1277 'date_modif_adh' => 'modif_date',
1278 'date_echeance' => 'due_date',
1279 'ct.date_enreg' => 'contrib_creation_date',
1280 'ct.date_debut_cotis' => 'contrib_begin_date',
1281 'ct.date_fin_cotis' => 'contrib_end_date'
1282 ];
1283
1284 foreach ($dates as $field => $property) {
1285 $bprop = "r{$property}_begin";
1286 if ($this->filters->$bprop) {
1287 $d = new \DateTime($this->filters->$bprop);
1288 $select->where->greaterThanOrEqualTo(
1289 $field,
1290 $d->format('Y-m-d')
1291 );
1292 }
1293 $eprop = "r{$property}_end";
1294 if ($this->filters->$eprop) {
1295 $d = new \DateTime($this->filters->$eprop);
1296 $select->where->lessThanOrEqualTo(
1297 $field,
1298 $d->format('Y-m-d')
1299 );
1300 }
1301 }
1302
1303 if ($this->filters->show_public_infos) {
1304 switch ($this->filters->show_public_infos) {
1305 case self::FILTER_W_PUBINFOS:
1306 $select->where('bool_display_info = true');
1307 break;
1308 case self::FILTER_WO_PUBINFOS:
1309 $select->where('bool_display_info = false');
1310 break;
1311 case self::FILTER_DC_PUBINFOS:
1312 //nothing to do here.
1313 break;
1314 }
1315 }
1316
1317 if ($this->filters->status) {
1318 $select->where->in(
1319 'a.id_statut',
1320 $this->filters->status
1321 );
1322 }
1323
1324 if (
1325 $this->filters->contrib_min_amount
1326 || $this->filters->contrib_max_amount
1327 ) {
1328 if ($this->filters->contrib_min_amount) {
1329 $select->where->greaterThanOrEqualTo(
1330 'ct.montant_cotis',
1331 $this->filters->contrib_min_amount
1332 );
1333 }
1334 if ($this->filters->contrib_max_amount) {
1335 $select->where->lessThanOrEqualTo(
1336 'ct.montant_cotis',
1337 $this->filters->contrib_max_amount
1338 );
1339 }
1340 }
1341
1342 if ($this->filters->contributions_types) {
1343 $select->where->in(
1344 'ct.id_type_cotis',
1345 $this->filters->contributions_types
1346 );
1347 }
1348
1349 if ($this->filters->payments_types) {
1350 $select->where->in(
1351 'ct.type_paiement_cotis',
1352 $this->filters->payments_types
1353 );
1354 }
1355
1356 if (
1357 count($this->filters->contrib_dynamic) > 0
1358 && !isset($this->filters->contrib_dynamic['empty'])
1359 ) {
1360 foreach ($this->filters->contrib_dynamic as $k => $cd) {
1361 $qry = '';
1362 $prefix = 'a.';
1363 $field = null;
1364 $qop = ' LIKE ';
1365
1366 if (is_array($cd)) {
1367 //dynamic choice spotted!
1368 $prefix = 'cdfc' . $k . '.';
1369 $qry = 'dfc.field_form = \'contrib\' AND ' .
1370 'dfc.field_id = ' . $k . ' AND ';
1371 $field = 'id';
1372 $select->where->in($prefix . $field, $cd);
1373 } else {
1374 //dynamic field spotted!
1375 $prefix = 'dfc.';
1376 $qry = 'dfc.field_form = \'contrib\' AND ' .
1377 'dfc.field_id = ' . $k . ' AND ';
1378 $field = 'field_val';
1379 $qry .= 'LOWER(' . $prefix . $field . ') ' .
1380 $qop . ' ';
1381 $select->where($qry . $zdb->platform->quoteValue('%' . strtolower($cd) . '%'));
1382 }
1383 }
1384 }
1385
1386 if (
1387 count($this->filters->free_search) > 0
1388 && !isset($this->filters->free_search['empty'])
1389 ) {
1390 foreach ($this->filters->free_search as $fs) {
1391 $fs['search'] = mb_strtolower($fs['search']);
1392 $qop = null;
1393 switch ($fs['qry_op']) {
1394 case AdvancedMembersList::OP_EQUALS:
1395 $qop = '=';
1396 break;
1397 case AdvancedMembersList::OP_CONTAINS:
1398 $qop = 'LIKE';
1399 $fs['search'] = '%' . $fs['search'] . '%';
1400 break;
1401 case AdvancedMembersList::OP_NOT_EQUALS:
1402 $qop = '!=';
1403 break;
1404 case AdvancedMembersList::OP_NOT_CONTAINS:
1405 $qop = 'NOT LIKE';
1406 $fs['search'] = '%' . $fs['search'] . '%';
1407 break;
1408 case AdvancedMembersList::OP_STARTS_WITH:
1409 $qop = 'LIKE';
1410 $fs['search'] = $fs['search'] . '%';
1411 break;
1412 case AdvancedMembersList::OP_ENDS_WITH:
1413 $qop = 'LIKE';
1414 $fs['search'] = '%' . $fs['search'];
1415 break;
1416 case AdvancedMembersList::OP_BEFORE:
1417 $qop = '<';
1418 break;
1419 case AdvancedMembersList::OP_AFTER:
1420 $qop = '>';
1421 break;
1422 default:
1423 Analog::log(
1424 'Unknown query operator: ' . $fs['qry_op'] .
1425 ' (will fallback to equals)',
1426 Analog::WARNING
1427 );
1428 $qop = '=';
1429 break;
1430 }
1431
1432 $qry = '';
1433 $prefix = 'a.';
1434 $dyn_field = false;
1435 if (strpos($fs['field'], 'dyn_') === 0) {
1436 // simple dynamic field spotted!
1437 $index = str_replace('dyn_', '', $fs['field']);
1438 $dyn_field = DynamicField::loadFieldType($zdb, (int)$index);
1439 $prefix = 'df' . $index . '.';
1440 $fs['field'] = 'val';
1441 }
1442
1443 if ($dyn_field && $dyn_field instanceof \Galette\DynamicFields\Boolean) {
1444 if ($fs['search'] != 0) {
1445 $qry .= $prefix . $fs['field'] . $qop . ' ' .
1446 $fs['search'];
1447 } else {
1448 $qry .= $prefix . $fs['field'] . ' IS NULL';
1449 }
1450 } elseif (!strncmp($fs['field'], 'bool_', strlen('bool_'))) {
1451 $qry .= $prefix . $fs['field'] . $qop . ' ' .
1452 $fs['search'];
1453 } elseif (
1454 $fs['qry_op'] === AdvancedMembersList::OP_BEFORE
1455 || $fs['qry_op'] === AdvancedMembersList::OP_AFTER
1456 ) {
1457 if ($prefix === 'a.') {
1458 //dates are OK in the main fields. no cast, just query!
1459 $qry .= $prefix . $fs['field'] . $qop . ' ' .
1460 $zdb->platform->quoteValue($fs['search']);
1461 } else {
1462 //dynamic dates are stored in their localized format :/
1463 //use current lang format to query for now
1464 if ($zdb->isPostgres()) {
1465 $fs['search'] = "to_date('" . $fs['search'] . "', 'YYYY-MM-DD')";
1466 $store_fmt = __("Y-m-d") === 'Y-m-d' ? 'YYYY-MM-DD' : 'DD/MM/YYYY';
1467 $qry .= "to_date('" . $prefix . $fs['field'] . "', '$store_fmt')";
1468 } else {
1469 $fs['search'] = "STR_TO_DATE('" . $fs['search'] . "', '%Y-%m-%d')";
1470 $store_fmt = __("Y-m-d") === 'Y-m-d' ? '%Y-%m-%d' : '%d/%m/%Y';
1471 $qry .= 'STR_TO_DATE(' . $prefix . $fs['field'] . ', \'' . $store_fmt . '\') ';
1472 }
1473
1474 $qry .= $qop . ' ' . $fs['search'];
1475 }
1476 } elseif ($fs['field'] == 'status_label') {
1477 $qry_pattern = '%p%field %op %value';
1478 $qry .= str_replace(
1479 [
1480 '%p',
1481 '%field',
1482 '%op',
1483 '%value'
1484 ],
1485 [
1486 'p.',
1487 'libelle_statut',
1488 $qop,
1489 $zdb->platform->quoteValue($fs['search'])
1490 ],
1491 $qry_pattern
1492 );
1493 } else {
1494 $qry .= 'LOWER(' . $prefix . $fs['field'] . ') ' .
1495 $qop . ' ' . $zdb->platform->quoteValue($fs['search']);
1496 }
1497
1498 if ($fs['log_op'] === AdvancedMembersList::OP_AND) {
1499 $select->where($qry);
1500 } elseif ($fs['log_op'] === AdvancedMembersList::OP_OR) {
1501 $select->where($qry, PredicateSet::OP_OR);
1502 }
1503 }
1504 }
1505
1506 return $select;
1507 }
1508
1509 /**
1510 * Login and password field cannot be empty.
1511 *
1512 * If those ones are not required, or if a file has been imported
1513 * (from a CSV file for example), we fill here random values.
1514 *
1515 * @return boolean
1516 */
1517 public function emptyLogins()
1518 {
1519 global $zdb;
1520
1521 try {
1522 $zdb->connection->beginTransaction();
1523 $select = $zdb->select(Adherent::TABLE);
1524 $select->columns(
1525 array('id_adh', 'login_adh', 'mdp_adh')
1526 )->where(
1527 array(
1528 'login_adh' => new Expression('NULL'),
1529 'login_adh' => '',
1530 'mdp_adh' => new Expression('NULL'),
1531 'mdp_adh' => ''
1532 ),
1533 PredicateSet::OP_OR
1534 );
1535
1536 $results = $zdb->execute($select);
1537
1538 $processed = 0;
1539 if ($results->count() > 0) {
1540 $update = $zdb->update(Adherent::TABLE);
1541 $update->set(
1542 array(
1543 'login_adh' => ':login',
1544 'mdp_adh' => ':pass'
1545 )
1546 )->where->equalTo(Adherent::PK, ':id');
1547
1548 $stmt = $zdb->sql->prepareStatementForSqlObject($update);
1549
1550 $p = new \Galette\Core\Password($zdb);
1551
1552 foreach ($results as $m) {
1553 $dirty = false;
1554 if (
1555 $m->login_adh == ''
1556 || !isset($m->login_adh)
1557 || $m->login_adh == 'NULL'
1558 ) {
1559 $m->login_adh = $p->makeRandomPassword(15);
1560 $dirty = true;
1561 }
1562
1563 if (
1564 $m->mdp_adh == ''
1565 || !isset($m->mdp_adh)
1566 || $m->mdp_adh == 'NULL'
1567 ) {
1568 $randomp = $p->makeRandomPassword(15);
1569 $m->mdp_adh = password_hash(
1570 $randomp,
1571 PASSWORD_BCRYPT
1572 );
1573 $dirty = true;
1574 }
1575
1576 if ($dirty === true) {
1577 /** Why where parameter is named where1 ?? */
1578 $stmt->execute(
1579 array(
1580 'login_adh' => $m->login_adh,
1581 'mdp_adh' => $m->mdp_adh,
1582 'where1' => $m->id_adh
1583 )
1584 );
1585 $processed++;
1586 }
1587 }
1588 }
1589 $zdb->connection->commit();
1590 $this->count = $processed;
1591 return true;
1592 } catch (\Exception $e) {
1593 $zdb->connection->rollBack();
1594 Analog::log(
1595 'An error occurred trying to retrieve members with ' .
1596 'empty logins/passwords (' . $e->getMessage(),
1597 Analog::ERROR
1598 );
1599 return false;
1600 }
1601 }
1602
1603 /**
1604 * Loads data to produce a Pie chart based on members state of dues
1605 *
1606 * @return void
1607 */
1608 public function getRemindersCount()
1609 {
1610 global $zdb;
1611
1612 $reminders = array();
1613
1614 $soon_date = new \DateTime();
1615 $soon_date->modify('+1 month');
1616
1617 $now = new \DateTime();
1618
1619 $select = $zdb->select(Adherent::TABLE, 'a');
1620 $select->columns(
1621 array(
1622 'cnt' => new Expression('count(a.' . Adherent::PK . ')')
1623 )
1624 );
1625
1626 $select->join(
1627 array('p' => PREFIX_DB . self::TABLE),
1628 'a.parent_id=p.' . self::PK,
1629 array(),
1630 $select::JOIN_LEFT
1631 );
1632
1633 $select->where
1634 ->lessThan('a.date_echeance', $soon_date->format('Y-m-d'))
1635 ->greaterThanOrEqualTo('a.date_echeance', $now->format('Y-m-d'));
1636 $select
1637 ->where('a.activite_adh=true')
1638 ->where('a.bool_exempt_adh=false');
1639
1640 $select_wo_mail = clone $select;
1641
1642 $select->where('(a.email_adh != \'\' OR p.email_adh != \'\')');
1643 $select_wo_mail->where('a.email_adh = \'\' AND p.email_adh = \'\'');
1644
1645 $results = $zdb->execute($select);
1646 $res = $results->current();
1647 $reminders['impending'] = $res->cnt;
1648
1649 $results_wo_mail = $zdb->execute($select_wo_mail);
1650 $res_wo_mail = $results_wo_mail->current();
1651 $reminders['nomail']['impending'] = $res_wo_mail->cnt;
1652
1653 $select = $zdb->select(Adherent::TABLE, 'a');
1654 $select->columns(
1655 array(
1656 'cnt' => new Expression('count(a.' . Adherent::PK . ')')
1657 )
1658 );
1659
1660 $select->join(
1661 array('p' => PREFIX_DB . self::TABLE),
1662 'a.parent_id=p.' . self::PK,
1663 array(),
1664 $select::JOIN_LEFT
1665 );
1666
1667 $select->where
1668 ->lessThan('a.date_echeance', $now->format('Y-m-d'));
1669 $select
1670 ->where('a.activite_adh=true')
1671 ->where('a.bool_exempt_adh=false');
1672
1673 $select_wo_mail = clone $select;
1674
1675 $select->where('(a.email_adh != \'\' OR p.email_adh != \'\')');
1676 $select_wo_mail->where('a.email_adh = \'\' AND p.email_adh = \'\'');
1677
1678 $results = $zdb->execute($select);
1679 $res = $results->current();
1680 $reminders['late'] = $res->cnt;
1681
1682 $results_wo_mail = $zdb->execute($select_wo_mail);
1683 $res_wo_mail = $results_wo_mail->current();
1684 $reminders['nomail']['late'] = $res_wo_mail->cnt;
1685
1686 return $reminders;
1687 }
1688
1689 /**
1690 * Get count for current query
1691 *
1692 * @return int
1693 */
1694 public function getCount()
1695 {
1696 return $this->count;
1697 }
1698
1699 /**
1700 * Get registered errors
1701 *
1702 * @return array
1703 */
1704 public function getErrors()
1705 {
1706 return $this->errors;
1707 }
1708
1709 /**
1710 * Get all existing emails
1711 *
1712 * @param Db $zdb Database instance
1713 *
1714 * @return array ['email' => 'id_adh']
1715 */
1716 public static function getEmails(Db $zdb)
1717 {
1718 $emails = [];
1719 $select = $zdb->select(self::TABLE);
1720 $select->columns([
1721 self::PK,
1722 'email_adh'
1723 ]);
1724 $select->where('email_adh != \'\' AND email_adh IS NOT NULL');
1725 $rows = $zdb->execute($select);
1726 foreach ($rows as $row) {
1727 $emails[$row->email_adh] = $row->{self::PK};
1728 }
1729 return $emails;
1730 }
1731
1732 /**
1733 * Get current filters
1734 *
1735 * @return MembersList
1736 */
1737 public function getFilters()
1738 {
1739 return $this->filters;
1740 }
1741
1742 /**
1743 * Get members list to instanciate dropdowns
1744 *
1745 * @param Db $zdb Database instance
1746 * @param integer $current Current member
1747 *
1748 * @return array
1749 */
1750 public function getSelectizedMembers(Db $zdb, $current = null)
1751 {
1752 $members = [];
1753 $required_fields = array(
1754 'id_adh',
1755 'nom_adh',
1756 'prenom_adh',
1757 'pseudo_adh'
1758 );
1759 $list_members = $this->getList(false, $required_fields);
1760
1761 if (count($list_members) > 0) {
1762 foreach ($list_members as $member) {
1763 $pk = Adherent::PK;
1764
1765 $members[$member->$pk] = Adherent::getNameWithCase(
1766 $member->nom_adh,
1767 $member->prenom_adh,
1768 false,
1769 $member->id_adh,
1770 $member->pseudo_adh
1771 );
1772 }
1773 }
1774
1775 //check if current attached member is part of the list
1776 if ($current !== null && !isset($members[$current])) {
1777 $members =
1778 [$current => Adherent::getSName($zdb, $current, true, true)] +
1779 $members
1780 ;
1781 }
1782
1783 return $members;
1784 }
1785 }