]> git.agnieray.net Git - galette.git/blob - galette/lib/Galette/Filters/AdvancedMembersList.php
3f988cf944688e34872918346c73671b477f3c98
[galette.git] / galette / lib / Galette / Filters / AdvancedMembersList.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Members list advanced filters
7 *
8 * PHP version 5
9 *
10 * Copyright © 2012-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 Filters
28 * @package Galette
29 *
30 * @author Johan Cwiklinski <johan@x-tnd.be>
31 * @copyright 2012-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 0.73dev 2012-10-16
36 */
37
38 namespace Galette\Filters;
39
40 use Analog\Analog;
41 use Galette\Entity\Status;
42 use Galette\Entity\ContributionsTypes;
43 use Galette\Entity\Contribution;
44 use Galette\Repository\Members;
45 use Galette\DynamicFields\DynamicField;
46 use Galette\Repository\PaymentTypes;
47
48 /**
49 * Members list filters and paginator
50 *
51 * @name AdvancedMembersList
52 * @category Filters
53 * @package Galette
54 *
55 * @author Johan Cwiklinski <johan@x-tnd.be>
56 * @copyright 2012-2014 The Galette Team
57 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
58 * @link http://galette.tuxfamily.org
59 */
60
61 class AdvancedMembersList extends MembersList
62 {
63
64 const OP_AND = 0;
65 const OP_OR = 1;
66
67 const OP_EQUALS = 0;
68 const OP_CONTAINS = 1;
69 const OP_NOT_EQUALS = 2;
70 const OP_NOT_CONTAINS = 3;
71 const OP_STARTS_WITH = 4;
72 const OP_ENDS_WITH = 5;
73 const OP_BEFORE = 6;
74 const OP_AFTER = 7;
75
76 private $_creation_date_begin;
77 private $_creation_date_end;
78 private $_modif_date_begin;
79 private $_modif_date_end;
80 private $_due_date_begin;
81 private $_due_date_end;
82 private $_birth_date_begin;
83 private $_birth_date_end;
84 private $_show_public_infos = Members::FILTER_DC_PUBINFOS;
85 private $_status = array();
86 private $_contrib_creation_date_begin;
87 private $_contrib_creation_date_end;
88 private $_contrib_begin_date_begin;
89 private $_contrib_begin_date_end;
90 private $_contrib_end_date_begin;
91 private $_contrib_end_date_end;
92 private $_contributions_types;
93 private $_payments_types;
94 private $_contrib_min_amount;
95 private $_contrib_max_amount;
96
97 protected $advancedmemberslist_fields = array(
98 'creation_date_begin',
99 'creation_date_end',
100 'modif_date_begin',
101 'modif_date_end',
102 'due_date_begin',
103 'due_date_end',
104 'birth_date_begin',
105 'birth_date_end',
106 'show_public_infos',
107 'status',
108 'contrib_creation_date_begin',
109 'contrib_creation_date_end',
110 'contrib_begin_date_begin',
111 'contrib_begin_date_end',
112 'contrib_end_date_begin',
113 'contrib_end_date_end',
114 'contributions_types',
115 'payments_types',
116 'contrib_min_amount',
117 'contrib_max_amount',
118 'contrib_dynamic',
119 'free_search',
120 'groups_search',
121 'groups_search_log_op'
122 );
123
124 protected $virtuals_advancedmemberslist_fields = array(
125 'rcreation_date_begin',
126 'rcreation_date_end',
127 'rmodif_date_begin',
128 'rmodif_date_end',
129 'rdue_date_begin',
130 'rdue_date_end',
131 'rbirth_date_begin',
132 'rbirth_date_end',
133 'rcontrib_creation_date_begin',
134 'rcontrib_creation_date_end',
135 'rcontrib_begin_date_begin',
136 'rcontrib_begin_date_end',
137 'rcontrib_end_date_begin',
138 'rcontrib_end_date_end',
139 'search_fields'
140 );
141
142 //an empty free search criteria to begin
143 private $_free_search = array(
144 'empty' => array(
145 'field' => '',
146 'search' => '',
147 'log_op' => self::OP_AND,
148 'qry_op' => self::OP_EQUALS
149 )
150 );
151
152 //an empty group search criteria to begin
153 private $_groups_search = array(
154 'empty' => array(
155 'group' => '',
156 )
157 );
158
159 //defaults to 'OR' for group search
160 private $_groups_search_log_op = self::OP_OR;
161
162
163 //an empty contributions dynamic field criteria to begin
164 private $_contrib_dynamic = array();
165
166 /**
167 * Default constructor
168 *
169 * @param MembersList $simple A simple filter search to keep
170 */
171 public function __construct($simple = null)
172 {
173 parent::__construct();
174 if ($simple instanceof MembersList) {
175 foreach ($this->pagination_fields as $pf) {
176 $this->$pf = $simple->$pf;
177 }
178 foreach ($this->memberslist_fields as $mlf) {
179 $this->$mlf = $simple->$mlf;
180 }
181 }
182 }
183
184 /**
185 * Do we want to filter within contributions?
186 *
187 * @return boolean
188 */
189 public function withinContributions()
190 {
191 if ($this->_contrib_creation_date_begin != null
192 || $this->_contrib_creation_date_end != null
193 || $this->_contrib_begin_date_begin != null
194 || $this->_contrib_begin_date_end != null
195 || $this->_contrib_end_date_begin != null
196 || $this->_contrib_begin_date_end != null
197 || $this->_contrib_min_amount != null
198 || $this->_contrib_max_amount != null
199 || count($this->_contrib_dynamic) > 0
200 || count($this->_contributions_types) > 0
201 || count($this->_payments_types) > 0
202 ) {
203 return true;
204 } else {
205 return false;
206 }
207 }
208
209 /**
210 * Reinit default parameters
211 *
212 * @return void
213 */
214 public function reinit()
215 {
216 parent::reinit();
217
218 $this->_creation_date_begin = null;
219 $this->_creation_date_end = null;
220 $this->_modif_date_begin = null;
221 $this->_modif_date_end = null;
222 $this->_due_date_begin = null;
223 $this->_due_date_end = null;
224 $this->_birth_date_begin = null;
225 $this->_birth_date_end = null;
226 $this->_show_public_infos = Members::FILTER_DC_PUBINFOS;
227 $this->_status = array();
228
229 $this->_contrib_creation_date_begin = null;
230 $this->_contrib_creation_date_end = null;
231 $this->_contrib_begin_date_begin = null;
232 $this->_contrib_begin_date_end = null;
233 $this->_contrib_end_date_begin = null;
234 $this->_contrib_begin_date_end = null;
235 $this->_contributions_types = array();
236 $this->_payments_types = array();
237
238 $this->_free_search = array(
239 'empty' => array(
240 'field' => '',
241 'search' => '',
242 'log_op' => self::OP_AND,
243 'qry_op' => self::OP_EQUALS
244 )
245 );
246
247 $this->_contrib_dynamic = array();
248
249 $this->_groups_search = array(
250 'empty' => array(
251 'group' => '',
252 )
253 );
254
255 $this->_groups_search_log_op = self::OP_OR;
256 }
257
258 /**
259 * Global getter method
260 *
261 * @param string $name name of the property we want to retrive
262 *
263 * @return object the called property
264 */
265 public function __get($name)
266 {
267
268 Analog::log(
269 '[AdvancedMembersList] Getting property `' . $name . '`',
270 Analog::DEBUG
271 );
272
273 if (in_array($name, $this->pagination_fields)
274 || in_array($name, $this->memberslist_fields)
275 ) {
276 return parent::__get($name);
277 } else {
278 if (in_array($name, $this->advancedmemberslist_fields)
279 || in_array($name, $this->virtuals_advancedmemberslist_fields)
280 ) {
281 $rname = '_' . $name;
282 switch ($name) {
283 case 'creation_date_begin':
284 case 'creation_date_end':
285 case 'modif_date_begin':
286 case 'modif_date_end':
287 case 'due_date_begin':
288 case 'due_date_end':
289 case 'birth_date_begin':
290 case 'birth_date_end':
291 case 'contrib_creation_date_begin':
292 case 'contrib_creation_date_end':
293 case 'contrib_begin_date_begin':
294 case 'contrib_begin_date_end':
295 case 'contrib_end_date_begin':
296 case 'contrib_end_date_end':
297 try {
298 if ($this->$rname !== null) {
299 $d = new \DateTime($this->$rname);
300 return $d->format(__("Y-m-d"));
301 }
302 } catch (\Exception $e) {
303 //oops, we've got a bad date :/
304 Analog::log(
305 'Bad date (' . $this->$rname . ') | ' .
306 $e->getMessage(),
307 Analog::INFO
308 );
309 return $this->$rname;
310 }
311 break;
312 case 'rcreation_date_begin':
313 case 'rcreation_date_end':
314 case 'rmodif_date_begin':
315 case 'rmodif_date_end':
316 case 'rdue_date_begin':
317 case 'rdue_date_end':
318 case 'rbirth_date_begin':
319 case 'rbirth_date_end':
320 case 'rcontrib_creation_date_begin':
321 case 'rcontrib_creation_date_end':
322 case 'rcontrib_begin_date_begin':
323 case 'rcontrib_begin_date_end':
324 case 'rcontrib_end_date_begin':
325 case 'rcontrib_end_date_end':
326 //same as above, but raw format
327 $rname = '_' . substr($name, 1);
328 return $this->$rname;
329 case 'search_fields':
330 $search_fields = array_merge($this->memberslist_fields, $this->advancedmemberslist_fields);
331 $key = array_search('selected', $search_fields);
332 unset($search_fields[$key]);
333 $key = array_search('unreachable', $search_fields);
334 unset($search_fields[$key]);
335 $key = array_search('query', $search_fields);
336 unset($search_fields[$key]);
337 return $search_fields;
338 }
339 return $this->$rname;
340 } else {
341 Analog::log(
342 '[AdvancedMembersList] Unable to get proprety `' .$name . '`',
343 Analog::WARNING
344 );
345 }
346 }
347 }
348
349 /**
350 * Global setter method
351 *
352 * @param string $name name of the property we want to assign a value to
353 * @param object $value a relevant value for the property
354 *
355 * @return void
356 */
357 public function __set($name, $value)
358 {
359 global $zdb, $preferences, $login;
360
361 if (in_array($name, $this->pagination_fields)
362 || in_array($name, $this->memberslist_fields)
363 ) {
364 parent::__set($name, $value);
365 } else {
366 Analog::log(
367 '[AdvancedMembersList] Setting property `' . $name . '`',
368 Analog::DEBUG
369 );
370
371 $prop = '_' . $name;
372
373 switch ($name) {
374 case 'creation_date_begin':
375 case 'creation_date_end':
376 case 'modif_date_begin':
377 case 'modif_date_end':
378 case 'due_date_begin':
379 case 'due_date_end':
380 case 'birth_date_begin':
381 case 'birth_date_end':
382 case 'contrib_creation_date_begin':
383 case 'contrib_creation_date_end':
384 case 'contrib_begin_date_begin':
385 case 'contrib_begin_date_end':
386 case 'contrib_end_date_begin':
387 case 'contrib_end_date_end':
388 if ($value !== null && trim($value) !== '') {
389 try {
390 $d = \DateTime::createFromFormat(__("Y-m-d"), $value);
391 if ($d === false) {
392 throw new \Exception('Incorrect format');
393 }
394 $this->$prop = $d->format('Y-m-d');
395 } catch (\Exception $e) {
396 Analog::log(
397 'Incorrect date format for ' . $name .
398 '! was: ' . $value,
399 Analog::WARNING
400 );
401 }
402 }
403 break;
404 case 'contrib_min_amount':
405 case 'contrib_max_amount':
406 if (is_float($value)) {
407 $this->$prop = $value;
408 } else {
409 if ($value !== null) {
410 Analog::log(
411 'Incorrect amount for ' . $name . '! ' .
412 'Should be a float (' . gettype($value) . ' given)',
413 Analog::WARNING
414 );
415 }
416 }
417 break;
418 case 'show_public_infos':
419 if (is_numeric($value)) {
420 $this->$prop = $value;
421 } else {
422 Analog::log(
423 '[AdvancedMembersList] Value for property `' . $name .
424 '` should be an integer (' . gettype($value) . ' given)',
425 Analog::WARNING
426 );
427 }
428 break;
429 case 'status':
430 if (!is_array($value)) {
431 $value = array($value);
432 }
433 $this->_status = array();
434 foreach ($value as $v) {
435 if (is_numeric($v)) {
436 //check status existence
437 $s = new Status($zdb);
438 $res = $s->get($v);
439 if ($res !== false) {
440 $this->_status[] = $v;
441 } else {
442 Analog::log(
443 'Status #' . $v . ' does not exists!',
444 Analog::WARNING
445 );
446 }
447 } else {
448 Analog::log(
449 '[AdvancedMembersList] Value for status filter should be an '
450 .'integer (' . gettype($v) . ' given',
451 Analog::WARNING
452 );
453 }
454 }
455 break;
456 case 'contributions_types':
457 if (!is_array($value)) {
458 $value = array($value);
459 }
460 $this->_contributions_types = array();
461 foreach ($value as $v) {
462 if (is_numeric($v)) {
463 //check type existence
464 $s = new ContributionsTypes($zdb);
465 $res = $s->get($v);
466 if ($res !== false) {
467 $this->_contributions_types[] = $v;
468 } else {
469 Analog::log(
470 'Contribution type #' . $v . ' does not exists!',
471 Analog::WARNING
472 );
473 }
474 } else {
475 Analog::log(
476 '[AdvancedMembersList] Value for contribution type '
477 . 'filter should be an integer (' . gettype($v) .
478 ' given',
479 Analog::WARNING
480 );
481 }
482 }
483 break;
484 case 'payments_types':
485 if (!is_array($value)) {
486 $value = array($value);
487 }
488 $this->_payments_types = array();
489 $ptypes = new PaymentTypes(
490 $zdb,
491 $preferences,
492 $login
493 );
494 $ptlist = $ptypes->getList();
495
496 foreach ($value as $v) {
497 if (is_numeric($v)) {
498 if (isset($ptlist[$v])) {
499 $this->_payments_types[] = $v;
500 } else {
501 Analog::log(
502 'Payment type #' . $v . ' does not exists!',
503 Analog::WARNING
504 );
505 }
506 } else {
507 Analog::log(
508 '[AdvancedMembersList] Value for payment type filter should be an '
509 .'integer (' . gettype($v) . ' given',
510 Analog::WARNING
511 );
512 }
513 }
514 break;
515 case 'free_search':
516 if (isset($this->_free_search['empty'])) {
517 unset($this->_free_search['empty']);
518 }
519
520 if ($this->isValidFreeSearch($value)) {
521 //should this happen?
522 $values = [$value];
523 } else {
524 $values = $value;
525 }
526
527 foreach ($values as $value) {
528 if ($this->isValidFreeSearch($value)) {
529 $id = $value['idx'];
530
531 //handle value according to type
532 switch ($value['type']) {
533 case DynamicField::DATE:
534 if ($value['search'] !== null && trim($value['search']) !== '') {
535 try {
536 $d = \DateTime::createFromFormat(__("Y-m-d"), $value['search']);
537 if ($d === false) {
538 throw new \Exception('Incorrect format');
539 }
540 $value['search'] = $d->format('Y-m-d');
541 } catch (\Exception $e) {
542 Analog::log(
543 'Incorrect date format for ' . $value['field'] .
544 '! was: ' . $value['search'],
545 Analog::WARNING
546 );
547 }
548 }
549 break;
550 }
551
552 $this->_free_search[$id] = $value;
553 } else {
554 Analog::log(
555 '[AdvancedMembersList] bad construct for free filter',
556 Analog::WARNING
557 );
558 }
559 }
560 break;
561 case 'contrib_dynamic':
562 if (is_array($value)) {
563 $this->_contrib_dynamic = $value;
564 } else {
565 Analog::log(
566 '[AdvancedMembersList] Value for dynamic contribution fields filter should be an '
567 .'array (' . gettype($value) . ' given',
568 Analog::WARNING
569 );
570 }
571 break;
572 case 'groups_search':
573 if (isset($this->_groups_search['empty'])) {
574 unset($this->_groups_search['empty']);
575 }
576 if (is_array($value)) {
577 if (isset($value['group'])
578 && isset($value['idx'])
579 ) {
580 $id = $value['idx'];
581 unset($value['idx']);
582 $this->_groups_search[$id] = $value;
583 } else {
584 Analog::log(
585 '[AdvancedMembersList] bad construct for group filter',
586 Analog::WARNING
587 );
588 }
589 } else {
590 Analog::log(
591 '[AdvancedMembersList] Value for group filter should be an '
592 .'array (' . gettype($value) . ' given',
593 Analog::WARNING
594 );
595 }
596 break;
597 case 'groups_search_log_op':
598 if ($value == self::OP_AND || $value == self::OP_OR) {
599 $this->_groups_search_log_op = $value;
600 } else {
601 Analog::log(
602 '[AdvancedMembersList] Value for group filter logical operator should be '
603 .' in [0,1] (' . gettype($value) . '-> '.$value.' given )',
604 Analog::WARNING
605 );
606 }
607 break;
608 default:
609 if (substr($name, 0, 4) === 'cds_'
610 || substr($name, 0, 5) === 'cdsc_'
611 ) {
612 if (is_array($value) || trim($value) !== '') {
613 $id = null;
614 if (substr($name, 0, 5) === 'cdsc_') {
615 $id = substr($name, 5, strlen($name));
616 } else {
617 $id = substr($name, 4, strlen($name));
618 }
619 $this->_contrib_dynamic[$id] = $value;
620 }
621 } else {
622 Analog::log(
623 '[AdvancedMembersList] Unable to set proprety `' .
624 $name . '`',
625 Analog::WARNING
626 );
627 }
628 break;
629 }
630 }
631 }
632
633 /**
634 * Validate free search internal array
635 *
636 * @param array $data Array to validate
637 *
638 * @return boolean
639 */
640 public static function isValidFreeSearch($data)
641 {
642 if (!is_array($data)) {
643 Analog::log(
644 '[AdvancedMembersList] Value for free filter should be an '
645 .'array (' . gettype($data) . ' given',
646 Analog::WARNING
647 );
648 return false;
649 }
650 return isset($data['field'])
651 && isset($data['search'])
652 && isset($data['log_op'])
653 && isset($data['qry_op'])
654 && isset($data['idx'])
655 && isset($data['type']);
656 }
657 }