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