]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Repository/Members.php
Remove 'svn' lines
[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 const TABLE = Adherent::TABLE;
74 const PK = Adherent::PK;
75
76 const ALL_ACCOUNTS = 0;
77 const ACTIVE_ACCOUNT = 1;
78 const INACTIVE_ACCOUNT = 2;
79
80 const SHOW_LIST = 0;
81 const SHOW_PUBLIC_LIST = 1;
82 const SHOW_ARRAY_LIST = 2;
83 const SHOW_STAFF = 3;
84 const SHOW_MANAGED = 4;
85 const SHOW_EXPORT = 5;
86
87 const FILTER_NAME = 0;
88 const FILTER_ADDRESS = 1;
89 const FILTER_MAIL = 2;
90 const FILTER_JOB = 3;
91 const FILTER_INFOS = 4;
92 const FILTER_DC_EMAIL = 5;
93 const FILTER_W_EMAIL = 6;
94 const FILTER_WO_EMAIL = 7;
95 const FILTER_COMPANY_NAME = 8;
96 const FILTER_DC_PUBINFOS = 9;
97 const FILTER_W_PUBINFOS = 10;
98 const FILTER_WO_PUBINFOS = 11;
99 const FILTER_NUMBER = 12;
100
101 const MEMBERSHIP_ALL = 0;
102 const MEMBERSHIP_UP2DATE = 3;
103 const MEMBERSHIP_NEARLY = 1;
104 const MEMBERSHIP_LATE = 2;
105 const MEMBERSHIP_NEVER = 4;
106 const MEMBERSHIP_STAFF = 5;
107 const MEMBERSHIP_ADMIN = 6;
108 const MEMBERSHIP_NONE = 7;
109
110 const ORDERBY_NAME = 0;
111 const ORDERBY_NICKNAME = 1;
112 const ORDERBY_STATUS = 2;
113 const ORDERBY_FEE_STATUS = 3;
114 const ORDERBY_MODIFDATE = 4;
115 const ORDERBY_ID = 5;
116
117 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 //$sep = ( TYPE_DB === 'pgsql' ) ? " || ' ' || " : ', " ", ';
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 // Search members who belong to any (OR) or all (AND) listed groups.
1170 // Idea is to build an array of members ID that fits groups selection
1171 // we will use in the final query.
1172 // The OR case is quite simple, AND is a bit more complex; since we must
1173 // check each member do belongs to all listed groups.
1174 if (
1175 count($this->filters->groups_search) > 0
1176 && !isset($this->filters->groups_search['empty'])
1177 ) {
1178 $groups_adh = [];
1179 $wheregroups = [];
1180
1181 foreach ($this->filters->groups_search as $gs) { // then add a row for each group
1182 $wheregroups[] = $gs['group'];
1183 }
1184
1185 $gselect = $zdb->select(Group::GROUPSUSERS_TABLE, 'gu');
1186 $gselect->columns(
1187 array('id_adh')
1188 )->join(
1189 array('g' => PREFIX_DB . Group::TABLE),
1190 'gu.id_group=g.' . Group::PK,
1191 array(),
1192 $select::JOIN_LEFT
1193 )->where(
1194 array(
1195 'g.id_group' => ':group',
1196 'g.parent_group' => ':pgroup'
1197 ),
1198 PredicateSet::OP_OR
1199 );
1200 $gselect->group(['gu.id_adh']);
1201
1202 $stmt = $zdb->sql->prepareStatementForSqlObject($gselect);
1203
1204 $mids = [];
1205 $ids = [];
1206 foreach ($this->filters->groups_search as $gs) { // then add a row for each ig/searched group pair
1207 /** Why where parameter is named where1 ?? */
1208 $gresults = $stmt->execute(
1209 array(
1210 'where1' => $gs['group'],
1211 'where2' => $gs['group']
1212 )
1213 );
1214
1215 switch ($this->filters->groups_search_log_op) {
1216 case AdvancedMembersList::OP_AND:
1217 foreach ($gresults as $gresult) {
1218 if (!isset($ids[$gresult['id_adh']])) {
1219 $ids[$gresult['id_adh']] = 0;
1220 }
1221 $ids[$gresult['id_adh']] += 1;
1222 }
1223 break;
1224 case AdvancedMembersList::OP_OR:
1225 foreach ($gresults as $gresult) {
1226 $mids[$gresult['id_adh']] = $gresult['id_adh'];
1227 }
1228 break;
1229 }
1230 }
1231
1232 if (count($ids)) {
1233 foreach ($ids as $id_adh => $count) {
1234 if ($count == count($wheregroups)) {
1235 $mids[$id_adh] = $id_adh;
1236 }
1237 }
1238 }
1239
1240 if (count($mids)) {
1241 //limit on found members
1242 $select->where->in('a.id_adh', $mids);
1243 } else {
1244 //no match in groups, end of game.
1245 $select->where('false = true');
1246 }
1247 }
1248
1249 if (
1250 $this->filters->rbirth_date_begin
1251 || $this->filters->rbirth_date_end
1252 ) {
1253 if ($this->filters->rbirth_date_begin) {
1254 $d = new \DateTime($this->filters->rbirth_date_begin);
1255 $select->where->greaterThanOrEqualTo(
1256 'ddn_adh',
1257 $d->format('Y-m-d')
1258 );
1259 }
1260 if ($this->filters->rbirth_date_end) {
1261 $d = new \DateTime($this->filters->rbirth_date_end);
1262 $select->where->lessThanOrEqualTo(
1263 'ddn_adh',
1264 $d->format('Y-m-d')
1265 );
1266 }
1267 }
1268
1269 if (
1270 $this->filters->rcreation_date_begin
1271 || $this->filters->rcreation_date_end
1272 ) {
1273 if ($this->filters->rcreation_date_begin) {
1274 $d = new \DateTime($this->filters->rcreation_date_begin);
1275 $select->where->greaterThanOrEqualTo(
1276 'date_crea_adh',
1277 $d->format('Y-m-d')
1278 );
1279 }
1280 if ($this->filters->rcreation_date_end) {
1281 $d = new \DateTime($this->filters->rcreation_date_end);
1282 $select->where->lessThanOrEqualTo(
1283 'date_crea_adh',
1284 $d->format('Y-m-d')
1285 );
1286 }
1287 }
1288
1289 if (
1290 $this->filters->rmodif_date_begin
1291 || $this->filters->rmodif_date_end
1292 ) {
1293 if ($this->filters->rmodif_date_begin) {
1294 $d = new \DateTime($this->filters->rmodif_date_begin);
1295 $select->where->greaterThanOrEqualTo(
1296 'date_modif_adh',
1297 $d->format('Y-m-d')
1298 );
1299 }
1300 if ($this->filters->rmodif_date_end) {
1301 $d = new \DateTime($this->filters->rmodif_date_end);
1302 $select->where->lessThanOrEqualTo(
1303 'date_modif_adh',
1304 $d->format('Y-m-d')
1305 );
1306 }
1307 }
1308
1309 if (
1310 $this->filters->rdue_date_begin
1311 || $this->filters->rdue_date_end
1312 ) {
1313 if ($this->filters->rdue_date_begin) {
1314 $d = new \DateTime($this->filters->rdue_date_begin);
1315 $select->where->greaterThanOrEqualTo(
1316 'date_echeance',
1317 $d->format('Y-m-d')
1318 );
1319 }
1320 if ($this->filters->rdue_date_end) {
1321 $d = new \DateTime($this->filters->rdue_date_end);
1322 $select->where->lessThanOrEqualTo(
1323 'date_echeance',
1324 $d->format('Y-m-d')
1325 );
1326 }
1327 }
1328
1329 if ($this->filters->show_public_infos) {
1330 switch ($this->filters->show_public_infos) {
1331 case self::FILTER_W_PUBINFOS:
1332 $select->where('bool_display_info = true');
1333 break;
1334 case self::FILTER_WO_PUBINFOS:
1335 $select->where('bool_display_info = false');
1336 break;
1337 case self::FILTER_DC_PUBINFOS:
1338 //nothing to do here.
1339 break;
1340 }
1341 }
1342
1343 if ($this->filters->status) {
1344 $select->where->in(
1345 'a.id_statut',
1346 $this->filters->status
1347 );
1348 }
1349
1350 if (
1351 $this->filters->rcontrib_creation_date_begin
1352 || $this->filters->rcontrib_creation_date_end
1353 ) {
1354 if ($this->filters->rcontrib_creation_date_begin) {
1355 $d = new \DateTime(
1356 $this->filters->rcontrib_creation_date_begin
1357 );
1358 $select->where->greaterThanOrEqualTo(
1359 'ct.date_enreg',
1360 $d->format('Y-m-d')
1361 );
1362 }
1363 if ($this->filters->rcontrib_creation_date_end) {
1364 $d = new \DateTime(
1365 $this->filters->rcontrib_creation_date_end
1366 );
1367 $select->where->lessThanOrEqualTo(
1368 'ct.date_enreg',
1369 $d->format('Y-m-d')
1370 );
1371 }
1372 }
1373
1374 if (
1375 $this->filters->rcontrib_begin_date_begin
1376 || $this->filters->rcontrib_begin_date_end
1377 ) {
1378 if ($this->filters->rcontrib_begin_date_begin) {
1379 $d = new \DateTime(
1380 $this->filters->rcontrib_begin_date_begin
1381 );
1382 $select->where->greaterThanOrEqualTo(
1383 'ct.date_debut_cotis',
1384 $d->format('Y-m-d')
1385 );
1386 }
1387 if ($this->filters->rcontrib_begin_date_end) {
1388 $d = new \DateTime(
1389 $this->filters->rcontrib_begin_date_end
1390 );
1391 $select->where->lessThanOrEqualTo(
1392 'ct.date_debut_cotis',
1393 $d->format('Y-m-d')
1394 );
1395 }
1396 }
1397
1398 if (
1399 $this->filters->rcontrib_end_date_begin
1400 || $this->filters->rcontrib_end_date_end
1401 ) {
1402 if ($this->filters->rcontrib_end_date_begin) {
1403 $d = new \DateTime(
1404 $this->filters->rcontrib_end_date_begin
1405 );
1406 $select->where->greaterThanOrEqualTo(
1407 'ct.date_fin_cotis',
1408 $d->format('Y-m-d')
1409 );
1410 }
1411 if ($this->filters->rcontrib_end_date_end) {
1412 $d = new \DateTime(
1413 $this->filters->rcontrib_end_date_end
1414 );
1415 $select->where->lessThanOrEqualTo(
1416 'ct.date_fin_cotis',
1417 $d->format('Y-m-d')
1418 );
1419 }
1420 }
1421
1422 if (
1423 $this->filters->contrib_min_amount
1424 || $this->filters->contrib_max_amount
1425 ) {
1426 if ($this->filters->contrib_min_amount) {
1427 $select->where->greaterThanOrEqualTo(
1428 'ct.montant_cotis',
1429 $this->filters->contrib_min_amount
1430 );
1431 }
1432 if ($this->filters->contrib_max_amount) {
1433 $select->where->lessThanOrEqualTo(
1434 'ct.montant_cotis',
1435 $this->filters->contrib_max_amount
1436 );
1437 }
1438 }
1439
1440 if ($this->filters->contributions_types) {
1441 $select->where->in(
1442 'ct.id_type_cotis',
1443 $this->filters->contributions_types
1444 );
1445 }
1446
1447 if ($this->filters->payments_types) {
1448 $select->where->in(
1449 'ct.type_paiement_cotis',
1450 $this->filters->payments_types
1451 );
1452 }
1453
1454 if (
1455 count($this->filters->contrib_dynamic) > 0
1456 && !isset($this->filters->contrib_dynamic['empty'])
1457 ) {
1458 foreach ($this->filters->contrib_dynamic as $k => $cd) {
1459 $qry = '';
1460 $prefix = 'a.';
1461 $field = null;
1462 $qop = ' LIKE ';
1463
1464 if (is_array($cd)) {
1465 //dynamic choice spotted!
1466 $prefix = 'cdfc' . $k . '.';
1467 $qry = 'dfc.field_form = \'contrib\' AND ' .
1468 'dfc.field_id = ' . $k . ' AND ';
1469 $field = 'id';
1470 $select->where->in($prefix . $field, $cd);
1471 } else {
1472 //dynamic field spotted!
1473 $prefix = 'dfc.';
1474 $qry = 'dfc.field_form = \'contrib\' AND ' .
1475 'dfc.field_id = ' . $k . ' AND ';
1476 $field = 'field_val';
1477 $qry .= 'LOWER(' . $prefix . $field . ') ' .
1478 $qop . ' ';
1479 $select->where($qry . $zdb->platform->quoteValue('%' . strtolower($cd) . '%'));
1480 }
1481 }
1482 }
1483
1484 if (
1485 count($this->filters->free_search) > 0
1486 && !isset($this->filters->free_search['empty'])
1487 ) {
1488 foreach ($this->filters->free_search as $fs) {
1489 $fs['search'] = mb_strtolower($fs['search']);
1490 $qop = null;
1491 switch ($fs['qry_op']) {
1492 case AdvancedMembersList::OP_EQUALS:
1493 $qop = '=';
1494 break;
1495 case AdvancedMembersList::OP_CONTAINS:
1496 $qop = 'LIKE';
1497 $fs['search'] = '%' . $fs['search'] . '%';
1498 break;
1499 case AdvancedMembersList::OP_NOT_EQUALS:
1500 $qop = '!=';
1501 break;
1502 case AdvancedMembersList::OP_NOT_CONTAINS:
1503 $qop = 'NOT LIKE';
1504 $fs['search'] = '%' . $fs['search'] . '%';
1505 break;
1506 case AdvancedMembersList::OP_STARTS_WITH:
1507 $qop = 'LIKE';
1508 $fs['search'] = $fs['search'] . '%';
1509 break;
1510 case AdvancedMembersList::OP_ENDS_WITH:
1511 $qop = 'LIKE';
1512 $fs['search'] = '%' . $fs['search'];
1513 break;
1514 case AdvancedMembersList::OP_BEFORE:
1515 $qop = '<';
1516 break;
1517 case AdvancedMembersList::OP_AFTER:
1518 $qop = '>';
1519 break;
1520 default:
1521 Analog::log(
1522 'Unknown query operator: ' . $fs['qry_op'] .
1523 ' (will fallback to equals)',
1524 Analog::WARNING
1525 );
1526 $qop = '=';
1527 break;
1528 }
1529
1530 $qry = '';
1531 $prefix = 'a.';
1532 $dyn_field = false;
1533 if (strpos($fs['field'], 'dyn_') === 0) {
1534 // simple dynamic field spotted!
1535 $index = str_replace('dyn_', '', $fs['field']);
1536 $dyn_field = DynamicField::loadFieldType($zdb, (int)$index);
1537 $prefix = 'df' . $index . '.';
1538 $fs['field'] = 'val';
1539 }
1540
1541 if ($dyn_field && $dyn_field instanceof \Galette\DynamicFields\Boolean) {
1542 if ($fs['search'] != 0) {
1543 $qry .= $prefix . $fs['field'] . $qop . ' ' .
1544 $fs['search'];
1545 } else {
1546 $qry .= $prefix . $fs['field'] . ' IS NULL';
1547 }
1548 } elseif (!strncmp($fs['field'], 'bool_', strlen('bool_'))) {
1549 $qry .= $prefix . $fs['field'] . $qop . ' ' .
1550 $fs['search'];
1551 } elseif (
1552 $fs['qry_op'] === AdvancedMembersList::OP_BEFORE
1553 || $fs['qry_op'] === AdvancedMembersList::OP_AFTER
1554 ) {
1555 if ($prefix === 'a.') {
1556 //dates are OK in the main fields. no cast, just query!
1557 $qry .= $prefix . $fs['field'] . $qop . ' ' .
1558 $zdb->platform->quoteValue($fs['search']);
1559 } else {
1560 //dynamic dates are stored in their localized format :/
1561 //use current lang format to query for now
1562 if ($zdb->isPostgres()) {
1563 $fs['search'] = "to_date('" . $fs['search'] . "', 'YYYY-MM-DD')";
1564 $store_fmt = __("Y-m-d") === 'Y-m-d' ? 'YYYY-MM-DD' : 'DD/MM/YYYY';
1565 $qry .= "to_date('" . $prefix . $fs['field'] . "', '$store_fmt')";
1566 } else {
1567 $fs['search'] = "STR_TO_DATE('" . $fs['search'] . "', '%Y-%m-%d')";
1568 $store_fmt = __("Y-m-d") === 'Y-m-d' ? '%Y-%m-%d' : '%d/%m/%Y';
1569 $qry .= 'STR_TO_DATE(' . $prefix . $fs['field'] . ', \'' . $store_fmt . '\') ';
1570 }
1571
1572 $qry .= $qop . ' ' . $fs['search'];
1573 }
1574 } elseif ($fs['field'] == 'status_label') {
1575 $qry_pattern = '%p%field %op %value';
1576 $qry .= str_replace(
1577 [
1578 '%p',
1579 '%field',
1580 '%op',
1581 '%value'
1582 ],
1583 [
1584 'p.',
1585 'libelle_statut',
1586 $qop,
1587 $zdb->platform->quoteValue($fs['search'])
1588 ],
1589 $qry_pattern
1590 );
1591 } else {
1592 $qry .= 'LOWER(' . $prefix . $fs['field'] . ') ' .
1593 $qop . ' ' . $zdb->platform->quoteValue($fs['search']);
1594 }
1595
1596 if ($fs['log_op'] === AdvancedMembersList::OP_AND) {
1597 $select->where($qry);
1598 } elseif ($fs['log_op'] === AdvancedMembersList::OP_OR) {
1599 $select->where($qry, PredicateSet::OP_OR);
1600 }
1601 }
1602 }
1603 }
1604
1605 return $select;
1606 } catch (\Exception $e) {
1607 Analog::log(
1608 __METHOD__ . ' | ' . $e->getMessage(),
1609 Analog::WARNING
1610 );
1611 throw $e;
1612 }
1613 }
1614
1615 /**
1616 * Login and password field cannot be empty.
1617 *
1618 * If those ones are not required, or if a file has been imported
1619 * (from a CSV file for example), we fill here random values.
1620 *
1621 * @return boolean
1622 */
1623 public function emptyLogins()
1624 {
1625 global $zdb;
1626
1627 try {
1628 $zdb->connection->beginTransaction();
1629 $select = $zdb->select(Adherent::TABLE);
1630 $select->columns(
1631 array('id_adh', 'login_adh', 'mdp_adh')
1632 )->where(
1633 array(
1634 'login_adh' => new Expression('NULL'),
1635 'login_adh' => '',
1636 'mdp_adh' => new Expression('NULL'),
1637 'mdp_adh' => ''
1638 ),
1639 PredicateSet::OP_OR
1640 );
1641
1642 $results = $zdb->execute($select);
1643
1644 $processed = 0;
1645 if ($results->count() > 0) {
1646 $update = $zdb->update(Adherent::TABLE);
1647 $update->set(
1648 array(
1649 'login_adh' => ':login',
1650 'mdp_adh' => ':pass'
1651 )
1652 )->where->equalTo(Adherent::PK, ':id');
1653
1654 $stmt = $zdb->sql->prepareStatementForSqlObject($update);
1655
1656 $p = new \Galette\Core\Password($zdb);
1657
1658 foreach ($results as $m) {
1659 $dirty = false;
1660 if (
1661 $m->login_adh == ''
1662 || !isset($m->login_adh)
1663 || $m->login_adh == 'NULL'
1664 ) {
1665 $m->login_adh = $p->makeRandomPassword(15);
1666 $dirty = true;
1667 }
1668
1669 if (
1670 $m->mdp_adh == ''
1671 || !isset($m->mdp_adh)
1672 || $m->mdp_adh == 'NULL'
1673 ) {
1674 $randomp = $p->makeRandomPassword(15);
1675 $m->mdp_adh = password_hash(
1676 $randomp,
1677 PASSWORD_BCRYPT
1678 );
1679 $dirty = true;
1680 }
1681
1682 if ($dirty === true) {
1683 /** Why where parameter is named where1 ?? */
1684 $stmt->execute(
1685 array(
1686 'login_adh' => $m->login_adh,
1687 'mdp_adh' => $m->mdp_adh,
1688 'where1' => $m->id_adh
1689 )
1690 );
1691 $processed++;
1692 }
1693 }
1694 }
1695 $zdb->connection->commit();
1696 $this->count = $processed;
1697 return true;
1698 } catch (\Exception $e) {
1699 $zdb->connection->rollBack();
1700 Analog::log(
1701 'An error occurred trying to retrieve members with ' .
1702 'empty logins/passwords (' . $e->getMessage(),
1703 Analog::ERROR
1704 );
1705 return false;
1706 }
1707 }
1708
1709 /**
1710 * Loads data to produce a Pie chart based on members state of dues
1711 *
1712 * @return void
1713 */
1714 public function getRemindersCount()
1715 {
1716 global $zdb;
1717
1718 $reminders = array();
1719
1720 $soon_date = new \DateTime();
1721 $soon_date->modify('+1 month');
1722
1723 $now = new \DateTime();
1724
1725 $select = $zdb->select(Adherent::TABLE, 'a');
1726 $select->columns(
1727 array(
1728 'cnt' => new Expression('count(a.' . Adherent::PK . ')')
1729 )
1730 );
1731
1732 $select->join(
1733 array('p' => PREFIX_DB . self::TABLE),
1734 'a.parent_id=p.' . self::PK,
1735 array(),
1736 $select::JOIN_LEFT
1737 );
1738
1739 $select->where
1740 ->lessThan('a.date_echeance', $soon_date->format('Y-m-d'))
1741 ->greaterThanOrEqualTo('a.date_echeance', $now->format('Y-m-d'));
1742 $select
1743 ->where('a.activite_adh=true')
1744 ->where('a.bool_exempt_adh=false');
1745
1746 $select_wo_mail = clone $select;
1747
1748 $select->where('(a.email_adh != \'\' OR p.email_adh != \'\')');
1749 $select_wo_mail->where('a.email_adh = \'\' AND p.email_adh = \'\'');
1750
1751 $results = $zdb->execute($select);
1752 $res = $results->current();
1753 $reminders['impending'] = $res->cnt;
1754
1755 $results_wo_mail = $zdb->execute($select_wo_mail);
1756 $res_wo_mail = $results_wo_mail->current();
1757 $reminders['nomail']['impending'] = $res_wo_mail->cnt;
1758
1759 $select = $zdb->select(Adherent::TABLE, 'a');
1760 $select->columns(
1761 array(
1762 'cnt' => new Expression('count(a.' . Adherent::PK . ')')
1763 )
1764 );
1765
1766 $select->join(
1767 array('p' => PREFIX_DB . self::TABLE),
1768 'a.parent_id=p.' . self::PK,
1769 array(),
1770 $select::JOIN_LEFT
1771 );
1772
1773 $select->where
1774 ->lessThan('a.date_echeance', $now->format('Y-m-d'));
1775 $select
1776 ->where('a.activite_adh=true')
1777 ->where('a.bool_exempt_adh=false');
1778
1779 $select_wo_mail = clone $select;
1780
1781 $select->where('(a.email_adh != \'\' OR p.email_adh != \'\')');
1782 $select_wo_mail->where('a.email_adh = \'\' AND p.email_adh = \'\'');
1783
1784 $results = $zdb->execute($select);
1785 $res = $results->current();
1786 $reminders['late'] = $res->cnt;
1787
1788 $results_wo_mail = $zdb->execute($select_wo_mail);
1789 $res_wo_mail = $results_wo_mail->current();
1790 $reminders['nomail']['late'] = $res_wo_mail->cnt;
1791
1792 return $reminders;
1793 }
1794
1795 /**
1796 * Get count for current query
1797 *
1798 * @return int
1799 */
1800 public function getCount()
1801 {
1802 return $this->count;
1803 }
1804
1805 /**
1806 * Get registered errors
1807 *
1808 * @return array
1809 */
1810 public function getErrors()
1811 {
1812 return $this->errors;
1813 }
1814
1815 /**
1816 * Get all existing emails
1817 *
1818 * @param Db $zdb Database instance
1819 *
1820 * @return array ['email' => 'id_adh']
1821 */
1822 public static function getEmails(Db $zdb)
1823 {
1824 $emails = [];
1825 $select = $zdb->select(self::TABLE);
1826 $select->columns([
1827 self::PK,
1828 'email_adh'
1829 ]);
1830 $select->where('email_adh != \'\' AND email_adh IS NOT NULL');
1831 $rows = $zdb->execute($select);
1832 foreach ($rows as $row) {
1833 $emails[$row->email_adh] = $row->{self::PK};
1834 }
1835 return $emails;
1836 }
1837
1838 /**
1839 * Get current filters
1840 *
1841 * @return MembersList
1842 */
1843 public function getFilters()
1844 {
1845 return $this->filters;
1846 }
1847
1848 /**
1849 * Get members list to instanciate dropdowns
1850 *
1851 * @param Db $zdb Database instance
1852 * @param integer $current Current member
1853 *
1854 * @return array
1855 */
1856 public function getSelectizedMembers(Db $zdb, $current = null)
1857 {
1858 $members = [];
1859 $required_fields = array(
1860 'id_adh',
1861 'nom_adh',
1862 'prenom_adh',
1863 'pseudo_adh'
1864 );
1865 $list_members = $this->getList(false, $required_fields);
1866
1867 if (count($list_members) > 0) {
1868 foreach ($list_members as $member) {
1869 $pk = Adherent::PK;
1870
1871 $members[$member->$pk] = Adherent::getNameWithCase(
1872 $member->nom_adh,
1873 $member->prenom_adh,
1874 false,
1875 $member->id_adh,
1876 $member->pseudo_adh
1877 );
1878 }
1879 }
1880
1881 //check if current attached member is part of the list
1882 if ($current !== null && !isset($members[$current])) {
1883 $members =
1884 [$current => Adherent::getSName($zdb, $current, true, true)] +
1885 $members
1886 ;
1887 }
1888
1889 return $members;
1890 }
1891 }