Split userfilter.php
[platal.git] / classes / userfilter / conditions.inc.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22 // {{{ abstract class UserFilterCondition
23 /** This class describe objects which filter users based
24 * on various parameters.
25 * The parameters of the filter must be given to the constructor.
26 * The buildCondition function is called by UserFilter when
27 * actually building the query. That function must call
28 * $uf->addWheteverFilter so that the UserFilter makes
29 * adequate joins. It must return the 'WHERE' condition to use
30 * with the filter.
31 */
32 abstract class UserFilterCondition implements PlFilterCondition
33 {
34 public function export()
35 {
36 throw new Exception("This class is not exportable");
37 }
38 }
39 // }}}
40
41 // {{{ class UFC_HasProfile
42 /** Filters users who have a profile
43 */
44 class UFC_HasProfile extends UserFilterCondition
45 {
46 public function buildCondition(PlFilter $uf)
47 {
48 $uf->requireProfiles();
49 return '$PID IS NOT NULL';
50 }
51 }
52 // }}}
53
54 // {{{ class UFC_AccountType
55 /** Filters users who have one of the given account types
56 */
57 class UFC_AccountType extends UserFilterCondition
58 {
59 private $types;
60
61 public function __construct()
62 {
63 $this->types = pl_flatten(func_get_args());
64 }
65
66 public function buildCondition(PlFilter $uf)
67 {
68 $uf->requireAccounts();
69 return XDB::format('a.type IN {?}', $this->types);
70 }
71 }
72 // }}}
73
74 // {{{ class UFC_AccountPerm
75 /** Filters users who have one of the given permissions
76 */
77 class UFC_AccountPerm extends UserFilterCondition
78 {
79 private $perms;
80
81 public function __construct()
82 {
83 $this->perms = pl_flatten(func_get_args());
84 }
85
86 public function buildCondition(PlFilter $uf)
87 {
88 $uf->requirePerms();
89 $conds = array();
90 foreach ($this->perms as $perm) {
91 $conds[] = XDB::format('FIND_IN_SET({?}, IF(a.user_perms IS NULL, at.perms,
92 CONCAT(at.perms, \',\', a.user_perms)))',
93 $perm);
94 }
95 if (empty($conds)) {
96 return self::COND_TRUE;
97 } else {
98 return implode(' OR ', $conds);
99 }
100 }
101 }
102 // }}}
103
104 // {{{ class UFC_Hruid
105 /** Filters users based on their hruid
106 * @param $val Either an hruid, or a list of those
107 */
108 class UFC_Hruid extends UserFilterCondition
109 {
110 private $hruids;
111
112 public function __construct()
113 {
114 $this->hruids = pl_flatten(func_get_args());
115 }
116
117 public function buildCondition(PlFilter $uf)
118 {
119 $uf->requireAccounts();
120 return XDB::format('a.hruid IN {?}', $this->hruids);
121 }
122 }
123 // }}}
124
125 // {{{ class UFC_Hrpid
126 /** Filters users based on the hrpid of their profiles
127 * @param $val Either an hrpid, or a list of those
128 */
129 class UFC_Hrpid extends UserFilterCondition
130 {
131 private $hrpids;
132
133 public function __construct()
134 {
135 $this->hrpids = pl_flatten(func_get_args());
136 }
137
138 public function buildCondition(PlFilter $uf)
139 {
140 $uf->requireProfiles();
141 return XDB::format('p.hrpid IN {?}', $this->hrpids);
142 }
143 }
144 // }}}
145
146 // {{{ class UFC_Ip
147 /** Filters users based on one of their last IPs
148 * @param $ip IP from which connection are checked
149 */
150 class UFC_Ip extends UserFilterCondition
151 {
152 private $ip;
153
154 public function __construct($ip)
155 {
156 $this->ip = $ip;
157 }
158
159 public function buildCondition(PlFilter $uf)
160 {
161 $sub = $uf->addLoggerFilter();
162 $ip = ip_to_uint($this->ip);
163 return XDB::format($sub . '.ip = {?} OR ' . $sub . '.forward_ip = {?}', $ip, $ip);
164 }
165 }
166 // }}}
167
168 // {{{ class UFC_Comment
169 class UFC_Comment extends UserFilterCondition
170 {
171 private $text;
172
173 public function __construct($text)
174 {
175 $this->text = $text;
176 }
177
178 public function buildCondition(PlFilter $uf)
179 {
180 $uf->requireProfiles();
181 return $uf->getVisibilityCondition('p.freetext_pub') . ' AND p.freetext ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->text);
182 }
183 }
184 // }}}
185
186 // {{{ class UFC_Promo
187 /** Filters users based on promotion
188 * @param $comparison Comparison operator (>, =, ...)
189 * @param $grade Formation on which to restrict, UserFilter::DISPLAY for "any formation"
190 * @param $promo Promotion on which the filter is based
191 */
192 class UFC_Promo extends UserFilterCondition
193 {
194
195 private $grade;
196 private $promo;
197 private $comparison;
198
199 public function __construct($comparison, $grade, $promo)
200 {
201 $this->grade = $grade;
202 $this->comparison = $comparison;
203 $this->promo = $promo;
204 if ($this->grade != UserFilter::DISPLAY) {
205 UserFilter::assertGrade($this->grade);
206 }
207 if ($this->grade == UserFilter::DISPLAY && $this->comparison != '=') {
208 // XXX: we might try to guess the grade from the first char of the promo and forbid only '<= 2004', but allow '<= X2004'
209 Platal::page()->killError("Il n'est pas possible d'appliquer la comparaison '" . $this->comparison . "' aux promotions sans spécifier de formation (X/M/D)");
210 }
211 }
212
213 public function buildCondition(PlFilter $uf)
214 {
215 if ($this->grade == UserFilter::DISPLAY) {
216 $sub = $uf->addDisplayFilter();
217 return XDB::format('pd' . $sub . '.promo ' . $this->comparison . ' {?}', $this->promo);
218 } else {
219 $sub = $uf->addEducationFilter(true, $this->grade);
220 $field = 'pe' . $sub . '.' . UserFilter::promoYear($this->grade);
221 return $field . ' IS NOT NULL AND ' . $field . ' ' . $this->comparison . ' ' . XDB::format('{?}', $this->promo);
222 }
223 }
224 }
225 // }}}
226
227 // {{{ class UFC_SchoolId
228 /** Filters users based on their shoold identifier
229 * @param type Parameter type (Xorg, AX, School)
230 * @param value School id value
231 */
232 class UFC_SchoolId extends UserFilterCondition
233 {
234 const AX = 'ax';
235 const Xorg = 'xorg';
236 const School = 'school';
237
238 private $type;
239 private $id;
240
241 static public function assertType($type)
242 {
243 if ($type != self::AX && $type != self::Xorg && $type != self::School) {
244 Platal::page()->killError("Type de matricule invalide: $type");
245 }
246 }
247
248 public function __construct($type, $id)
249 {
250 $this->type = $type;
251 $this->id = $id;
252 self::assertType($type);
253 }
254
255 public function buildCondition(PlFilter $uf)
256 {
257 $uf->requireProfiles();
258 $id = $this->id;
259 $type = $this->type;
260 if ($type == self::School) {
261 $type = self::Xorg;
262 $id = Profile::getXorgId($id);
263 }
264 return XDB::format('p.' . $type . '_id = {?}', $id);
265 }
266 }
267 // }}}
268
269 // {{{ class UFC_EducationSchool
270 /** Filters users by formation
271 * @param $val The formation to search (either ID or array of IDs)
272 */
273 class UFC_EducationSchool extends UserFilterCondition
274 {
275 private $val;
276
277 public function __construct()
278 {
279 $this->val = pl_flatten(func_get_args());
280 }
281
282 public function buildCondition(PlFilter $uf)
283 {
284 $sub = $uf->addEducationFilter();
285 return XDB::format('pe' . $sub . '.eduid IN {?}', $this->val);
286 }
287 }
288 // }}}
289
290 // {{{ class UFC_EducationDegree
291 class UFC_EducationDegree extends UserFilterCondition
292 {
293 private $diploma;
294
295 public function __construct()
296 {
297 $this->diploma = pl_flatten(func_get_args());
298 }
299
300 public function buildCondition(PlFilter $uf)
301 {
302 $sub = $uf->addEducationFilter();
303 return XDB::format('pe' . $sub . '.degreeid IN {?}', $this->diploma);
304 }
305 }
306 // }}}
307
308 // {{{ class UFC_EducationField
309 class UFC_EducationField extends UserFilterCondition
310 {
311 private $val;
312
313 public function __construct()
314 {
315 $this->val = pl_flatten(func_get_args());
316 }
317
318 public function buildCondition(PlFilter $uf)
319 {
320 $sub = $uf->addEducationFilter();
321 return XDB::format('pe' . $sub . '.fieldid IN {?}', $this->val);
322 }
323 }
324 // }}}
325
326 // {{{ class UFC_Name
327 /** Filters users based on name
328 * @param $type Type of name field on which filtering is done (firstname, lastname...)
329 * @param $text Text on which to filter
330 * @param $mode Flag indicating search type (prefix, suffix, with particule...)
331 */
332 class UFC_Name extends UserFilterCondition
333 {
334 const EXACT = XDB::WILDCARD_EXACT; // 0x000
335 const PREFIX = XDB::WILDCARD_PREFIX; // 0x001
336 const SUFFIX = XDB::WILDCARD_SUFFIX; // 0x002
337 const CONTAINS = XDB::WILDCARD_CONTAINS; // 0x003
338 const PARTICLE = 0x004;
339 const VARIANTS = 0x008;
340
341 private $type;
342 private $text;
343 private $mode;
344
345 public function __construct($type, $text, $mode)
346 {
347 $this->type = $type;
348 $this->text = $text;
349 $this->mode = $mode;
350 }
351
352 private function buildNameQuery($type, $variant, $where, UserFilter $uf)
353 {
354 $sub = $uf->addNameFilter($type, $variant);
355 return str_replace('$ME', 'pn' . $sub, $where);
356 }
357
358 public function buildCondition(PlFilter $uf)
359 {
360 $left = '$ME.name';
361 if (($this->mode & self::PARTICLE) == self::PARTICLE) {
362 $left = 'CONCAT($ME.particle, \' \', $ME.name)';
363 }
364 $right = XDB::formatWildcards($this->mode & self::CONTAINS, $this->text);
365
366 $cond = $left . $right;
367 $conds = array($this->buildNameQuery($this->type, null, $cond, $uf));
368 if (($this->mode & self::VARIANTS) != 0 && isset(Profile::$name_variants[$this->type])) {
369 foreach (Profile::$name_variants[$this->type] as $var) {
370 $conds[] = $this->buildNameQuery($this->type, $var, $cond, $uf);
371 }
372 }
373 return implode(' OR ', $conds);
374 }
375 }
376 // }}}
377
378 // {{{ class UFC_NameTokens
379 /** Selects users based on tokens in their name (for quicksearch)
380 * @param $tokens An array of tokens to search
381 * @param $flags Flags the tokens must have (e.g 'public' for public search)
382 * @param $soundex (bool) Whether those tokens are fulltext or soundex
383 */
384 class UFC_NameTokens extends UserFilterCondition
385 {
386 /* Flags */
387 const FLAG_PUBLIC = 'public';
388
389 private $tokens;
390 private $flags;
391 private $soundex;
392 private $exact;
393
394 public function __construct($tokens, $flags = array(), $soundex = false, $exact = false)
395 {
396 if (is_array($tokens)) {
397 $this->tokens = $tokens;
398 } else {
399 $this->tokens = array($tokens);
400 }
401 if (is_array($flags)) {
402 $this->flags = $flags;
403 } else {
404 $this->flags = array($flags);
405 }
406 $this->soundex = $soundex;
407 $this->exact = $exact;
408 }
409
410 public function buildCondition(PlFilter $uf)
411 {
412 $conds = array();
413 foreach ($this->tokens as $i => $token) {
414 $sub = $uf->addNameTokensFilter($token);
415 if ($this->soundex) {
416 $c = XDB::format($sub . '.soundex = {?}', soundex_fr($token));
417 } else if ($this->exact) {
418 $c = XDB::format($sub . '.token = {?}', $token);
419 } else {
420 $c = $sub . '.token ' . XDB::formatWildcards(XDB::WILDCARD_PREFIX, $token);
421 }
422 if ($this->flags != null) {
423 $c .= XDB::format(' AND ' . $sub . '.flags IN {?}', $this->flags);
424 }
425 $conds[] = $c;
426 }
427
428 return implode(' AND ', $conds);
429 }
430 }
431 // }}}
432
433 // {{{ class UFC_Nationality
434 class UFC_Nationality extends UserFilterCondition
435 {
436 private $val;
437
438 public function __construct()
439 {
440 $this->val = pl_flatten(func_get_args());
441 }
442
443 public function buildCondition(PlFilter $uf)
444 {
445 $uf->requireProfiles();
446 $nat = XDB::formatArray($this->val);
447 $conds = array(
448 'p.nationality1 IN ' . $nat,
449 'p.nationality2 IN ' . $nat,
450 'p.nationality3 IN ' . $nat,
451 );
452 return implode(' OR ', $conds);
453 }
454 }
455 // }}}
456
457 // {{{ class UFC_Dead
458 /** Filters users based on death date
459 * @param $comparison Comparison operator
460 * @param $date Date to which death date should be compared (DateTime object, string or timestamp)
461 */
462 class UFC_Dead extends UserFilterCondition
463 {
464 private $comparison;
465 private $date;
466
467 public function __construct($comparison = null, $date = null)
468 {
469 $this->comparison = $comparison;
470 $this->date = make_datetime($date);
471 }
472
473 public function buildCondition(PlFilter $uf)
474 {
475 $uf->requireProfiles();
476 $str = 'p.deathdate IS NOT NULL';
477 if (!is_null($this->comparison)) {
478 $str .= ' AND p.deathdate ' . $this->comparison . ' ' . XDB::format('{?}', $this->date->format('Y-m-d'));
479 }
480 return $str;
481 }
482 }
483 // }}}
484
485 // {{{ class UFC_Registered
486 /** Filters users based on registration state
487 * @param $active Whether we want to use only "active" users (i.e with a valid redirection)
488 * @param $comparison Comparison operator
489 * @param $date Date to which users registration date should be compared
490 */
491 class UFC_Registered extends UserFilterCondition
492 {
493 private $active;
494 private $comparison;
495 private $date;
496
497 public function __construct($active = false, $comparison = null, $date = null)
498 {
499 $this->active = $active;
500 $this->comparison = $comparison;
501 $this->date = make_datetime($date);
502 }
503
504 public function buildCondition(PlFilter $uf)
505 {
506 $uf->requireAccounts();
507 if ($this->active) {
508 $date = '$UID IS NOT NULL AND a.state = \'active\'';
509 } else {
510 $date = '$UID IS NOT NULL AND a.state != \'pending\'';
511 }
512 if (!is_null($this->comparison)) {
513 $date .= ' AND a.registration_date != \'0000-00-00 00:00:00\' AND a.registration_date ' . $this->comparison . ' ' . XDB::format('{?}', $this->date->format('Y-m-d'));
514 }
515 return $date;
516 }
517 }
518 // }}}
519
520 // {{{ class UFC_ProfileUpdated
521 /** Filters users based on profile update date
522 * @param $comparison Comparison operator
523 * @param $date Date to which profile update date must be compared
524 */
525 class UFC_ProfileUpdated extends UserFilterCondition
526 {
527 private $comparison;
528 private $date;
529
530 public function __construct($comparison = null, $date = null)
531 {
532 $this->comparison = $comparison;
533 $this->date = $date;
534 }
535
536 public function buildCondition(PlFilter $uf)
537 {
538 $uf->requireProfiles();
539 return 'p.last_change ' . $this->comparison . XDB::format(' {?}', date('Y-m-d H:i:s', $this->date));
540 }
541 }
542 // }}}
543
544 // {{{ class UFC_Birthday
545 /** Filters users based on next birthday date
546 * @param $comparison Comparison operator
547 * @param $date Date to which users next birthday date should be compared
548 */
549 class UFC_Birthday extends UserFilterCondition
550 {
551 private $comparison;
552 private $date;
553
554 public function __construct($comparison = null, $date = null)
555 {
556 $this->comparison = $comparison;
557 $this->date = $date;
558 }
559
560 public function buildCondition(PlFilter $uf)
561 {
562 $uf->requireProfiles();
563 return 'p.next_birthday ' . $this->comparison . XDB::format(' {?}', date('Y-m-d', $this->date));
564 }
565 }
566 // }}}
567
568 // {{{ class UFC_Sex
569 /** Filters users based on sex
570 * @parm $sex One of User::GENDER_MALE or User::GENDER_FEMALE, for selecting users
571 */
572 class UFC_Sex extends UserFilterCondition
573 {
574 private $sex;
575 public function __construct($sex)
576 {
577 $this->sex = $sex;
578 }
579
580 public function buildCondition(PlFilter $uf)
581 {
582 if ($this->sex != User::GENDER_MALE && $this->sex != User::GENDER_FEMALE) {
583 return self::COND_FALSE;
584 } else {
585 $uf->requireProfiles();
586 return XDB::format('p.sex = {?}', $this->sex == User::GENDER_FEMALE ? 'female' : 'male');
587 }
588 }
589 }
590 // }}}
591
592 // {{{ class UFC_Group
593 /** Filters users based on group membership
594 * @param $group Group whose members we are selecting
595 * @param $anim Whether to restrict selection to animators of that group
596 */
597 class UFC_Group extends UserFilterCondition
598 {
599 private $group;
600 private $anim;
601 public function __construct($group, $anim = false)
602 {
603 $this->group = $group;
604 $this->anim = $anim;
605 }
606
607 public function buildCondition(PlFilter $uf)
608 {
609 // Groups have AX visibility.
610 if ($uf->getVisibilityLevel() == ProfileVisibility::VIS_PUBLIC) {
611 return self::COND_TRUE;
612 }
613 $sub = $uf->addGroupFilter($this->group);
614 $where = 'gpm' . $sub . '.perms IS NOT NULL';
615 if ($this->anim) {
616 $where .= ' AND gpm' . $sub . '.perms = \'admin\'';
617 }
618 return $where;
619 }
620 }
621 // }}}
622
623 // {{{ class UFC_Binet
624 /** Selects users based on their belonging to a given (list of) binet
625 * @param $binet either a binet_id or an array of binet_ids
626 */
627 class UFC_Binet extends UserFilterCondition
628 {
629 private $val;
630
631 public function __construct()
632 {
633 $this->val = pl_flatten(func_get_args());
634 }
635
636 public function buildCondition(PlFilter $uf)
637 {
638 // Binets are private.
639 if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) {
640 return self::CONF_TRUE;
641 }
642 $sub = $uf->addBinetsFilter();
643 return XDB::format($sub . '.binet_id IN {?}', $this->val);
644 }
645 }
646 // }}}
647
648 // {{{ class UFC_Section
649 /** Selects users based on section
650 * @param $section ID of the section
651 */
652 class UFC_Section extends UserFilterCondition
653 {
654 private $section;
655
656 public function __construct()
657 {
658 $this->section = pl_flatten(func_get_args());
659 }
660
661 public function buildCondition(PlFilter $uf)
662 {
663 // Sections are private.
664 if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) {
665 return self::CONF_TRUE;
666 }
667 $uf->requireProfiles();
668 return XDB::format('p.section IN {?}', $this->section);
669 }
670 }
671 // }}}
672
673 // {{{ class UFC_Email
674 /** Filters users based on an email or a list of emails
675 * @param $emails List of emails whose owner must be selected
676 */
677 class UFC_Email extends UserFilterCondition
678 {
679 private $emails;
680 public function __construct()
681 {
682 $this->emails = pl_flatten(func_get_args());
683 }
684
685 public function buildCondition(PlFilter $uf)
686 {
687 $foreign = array();
688 $virtual = array();
689 $aliases = array();
690 $cond = array();
691
692 if (count($this->emails) == 0) {
693 return PlFilterCondition::COND_TRUE;
694 }
695
696 foreach ($this->emails as $entry) {
697 if (User::isForeignEmailAddress($entry)) {
698 $foreign[] = $entry;
699 } else if (User::isVirtualEmailAddress($entry)) {
700 $virtual[] = $entry;
701 } else {
702 @list($user, $domain) = explode('@', $entry);
703 $aliases[] = $user;
704 }
705 }
706
707 if (count($foreign) > 0) {
708 $sub = $uf->addEmailRedirectFilter($foreign);
709 $cond[] = XDB::format('e' . $sub . '.email IS NOT NULL OR a.email IN {?}', $foreign);
710 }
711 if (count($virtual) > 0) {
712 $sub = $uf->addVirtualEmailFilter($virtual);
713 $cond[] = 'vr' . $sub . '.redirect IS NOT NULL';
714 }
715 if (count($aliases) > 0) {
716 $sub = $uf->addAliasFilter($aliases);
717 $cond[] = 'al' . $sub . '.alias IS NOT NULL';
718 }
719 return '(' . implode(') OR (', $cond) . ')';
720 }
721 }
722 // }}}
723
724 // {{{ class UFC_Address
725 abstract class UFC_Address extends UserFilterCondition
726 {
727 /** Valid address type ('hq' is reserved for company addresses)
728 */
729 const TYPE_HOME = 1;
730 const TYPE_PRO = 2;
731 const TYPE_ANY = 3;
732
733 /** Text for these types
734 */
735 protected static $typetexts = array(
736 self::TYPE_HOME => 'home',
737 self::TYPE_PRO => 'pro',
738 );
739
740 protected $type;
741
742 /** Flags for addresses
743 */
744 const FLAG_CURRENT = 0x0001;
745 const FLAG_TEMP = 0x0002;
746 const FLAG_SECOND = 0x0004;
747 const FLAG_MAIL = 0x0008;
748 const FLAG_CEDEX = 0x0010;
749
750 // Binary OR of those flags
751 const FLAG_ANY = 0x001F;
752
753 /** Text of these flags
754 */
755 protected static $flagtexts = array(
756 self::FLAG_CURRENT => 'current',
757 self::FLAG_TEMP => 'temporary',
758 self::FLAG_SECOND => 'secondary',
759 self::FLAG_MAIL => 'mail',
760 self::FLAG_CEDEX => 'cedex',
761 );
762
763 protected $flags;
764
765 public function __construct($type = null, $flags = null)
766 {
767 $this->type = $type;
768 $this->flags = $flags;
769 }
770
771 protected function initConds($sub, $vis_cond)
772 {
773 $conds = array($vis_cond);
774
775 $types = array();
776 foreach (self::$typetexts as $flag => $type) {
777 if ($flag & $this->type) {
778 $types[] = $type;
779 }
780 }
781 if (count($types)) {
782 $conds[] = XDB::format($sub . '.type IN {?}', $types);
783 }
784
785 if ($this->flags != self::FLAG_ANY) {
786 foreach(self::$flagtexts as $flag => $text) {
787 if ($flag & $this->flags) {
788 $conds[] = 'FIND_IN_SET(' . XDB::format('{?}', $text) . ', ' . $sub . '.flags)';
789 }
790 }
791 }
792 return $conds;
793 }
794
795 }
796 // }}}
797
798 // {{{ class UFC_AddressText
799 /** Select users based on their address, using full text search
800 * @param $text Text for filter in fulltext search
801 * @param $textSearchMode Mode for search (one of XDB::WILDCARD_*)
802 * @param $type Filter on address type
803 * @param $flags Filter on address flags
804 * @param $country Filter on address country
805 * @param $locality Filter on address locality
806 */
807 class UFC_AddressText extends UFC_Address
808 {
809
810 private $text;
811 private $textSearchMode;
812
813 public function __construct($text = null, $textSearchMode = XDB::WILDCARD_CONTAINS,
814 $type = null, $flags = self::FLAG_ANY, $country = null, $locality = null)
815 {
816 parent::__construct($type, $flags);
817 $this->text = $text;
818 $this->textSearchMode = $textSearchMode;
819 $this->country = $country;
820 $this->locality = $locality;
821 }
822
823 private function mkMatch($txt)
824 {
825 return XDB::formatWildcards($this->textSearchMode, $txt);
826 }
827
828 public function buildCondition(PlFilter $uf)
829 {
830 $sub = $uf->addAddressFilter();
831 $conds = $this->initConds($sub, $uf->getVisibilityCondition($sub . '.pub'));
832 if ($this->text != null) {
833 $conds[] = $sub . '.text' . $this->mkMatch($this->text);
834 }
835
836 if ($this->country != null) {
837 $subc = $uf->addAddressCountryFilter();
838 $subconds = array();
839 $subconds[] = $subc . '.country' . $this->mkMatch($this->country);
840 $subconds[] = $subc . '.countryFR' . $this->mkMatch($this->country);
841 $conds[] = implode(' OR ', $subconds);
842 }
843
844 if ($this->locality != null) {
845 $subl = $uf->addAddressLocalityFilter();
846 $conds[] = $subl . '.name' . $this->mkMatch($this->locality);
847 }
848
849 return implode(' AND ', $conds);
850 }
851 }
852 // }}}
853
854 // {{{ class UFC_AddressField
855 /** Filters users based on their address,
856 * @param $val Either a code for one of the fields, or an array of such codes
857 * @param $fieldtype The type of field to look for
858 * @param $type Filter on address type
859 * @param $flags Filter on address flags
860 */
861 class UFC_AddressField extends UFC_Address
862 {
863 const FIELD_COUNTRY = 1;
864 const FIELD_ADMAREA = 2;
865 const FIELD_SUBADMAREA = 3;
866 const FIELD_LOCALITY = 4;
867 const FIELD_ZIPCODE = 5;
868
869 /** Data of the filter
870 */
871 private $val;
872 private $fieldtype;
873
874 public function __construct($val, $fieldtype, $type = null, $flags = self::FLAG_ANY)
875 {
876 parent::__construct($type, $flags);
877
878 if (!is_array($val)) {
879 $val = array($val);
880 }
881 $this->val = $val;
882 $this->fieldtype = $fieldtype;
883 }
884
885 public function buildCondition(PlFilter $uf)
886 {
887 $sub = $uf->addAddressFilter();
888 $conds = $this->initConds($sub, $uf->getVisibilityCondition($sub . '.pub'));
889
890 switch ($this->fieldtype) {
891 case self::FIELD_COUNTRY:
892 $field = 'countryId';
893 break;
894 case self::FIELD_ADMAREA:
895 $field = 'administrativeAreaId';
896 break;
897 case self::FIELD_SUBADMAREA:
898 $field = 'subAdministrativeAreaId';
899 break;
900 case self::FIELD_LOCALITY:
901 $field = 'localityId';
902 break;
903 case self::FIELD_ZIPCODE:
904 $field = 'postalCode';
905 break;
906 default:
907 Platal::page()->killError('Invalid address field type: ' . $this->fieldtype);
908 }
909 $conds[] = XDB::format($sub . '.' . $field . ' IN {?}', $this->val);
910
911 return implode(' AND ', $conds);
912 }
913 }
914 // }}}
915
916 // {{{ class UFC_Corps
917 /** Filters users based on the corps they belong to
918 * @param $corps Corps we are looking for (abbreviation)
919 * @param $type Whether we search for original or current corps
920 */
921 class UFC_Corps extends UserFilterCondition
922 {
923 const CURRENT = 1;
924 const ORIGIN = 2;
925
926 private $corps;
927 private $type;
928
929 public function __construct($corps, $type = self::CURRENT)
930 {
931 $this->corps = $corps;
932 $this->type = $type;
933 }
934
935 public function buildCondition(PlFilter $uf)
936 {
937 /** Tables shortcuts:
938 * pc for profile_corps,
939 * pceo for profile_corps_enum - orginal
940 * pcec for profile_corps_enum - current
941 */
942 $sub = $uf->addCorpsFilter($this->type);
943 $cond = $sub . '.abbreviation = ' . $corps;
944 $cond .= ' AND ' . $uf->getVisibilityCondition($sub . '.corps_pub');
945 return $cond;
946 }
947 }
948 // }}}
949
950 // {{{ class UFC_Corps_Rank
951 /** Filters users based on their rank in the corps
952 * @param $rank Rank we are looking for (abbreviation)
953 */
954 class UFC_Corps_Rank extends UserFilterCondition
955 {
956 private $rank;
957 public function __construct($rank)
958 {
959 $this->rank = $rank;
960 }
961
962 public function buildCondition(PlFilter $uf)
963 {
964 /** Tables shortcuts:
965 * pc for profile_corps
966 * pcr for profile_corps_rank
967 */
968 $sub = $uf->addCorpsRankFilter();
969 $cond = $sub . '.abbreviation = ' . $rank;
970 // XXX(x2006barrois): find a way to get rid of that hardcoded
971 // reference to 'pc'.
972 $cond .= ' AND ' . $uf->getVisibilityCondition('pc.corps_pub');
973 return $cond;
974 }
975 }
976 // }}}
977
978 // {{{ class UFC_Job_Company
979 /** Filters users based on the company they belong to
980 * @param $type The field being searched (self::JOBID, self::JOBNAME or self::JOBACRONYM)
981 * @param $value The searched value
982 */
983 class UFC_Job_Company extends UserFilterCondition
984 {
985 const JOBID = 'id';
986 const JOBNAME = 'name';
987 const JOBACRONYM = 'acronym';
988
989 private $type;
990 private $value;
991
992 public function __construct($type, $value)
993 {
994 $this->assertType($type);
995 $this->type = $type;
996 $this->value = $value;
997 }
998
999 private function assertType($type)
1000 {
1001 if ($type != self::JOBID && $type != self::JOBNAME && $type != self::JOBACRONYM) {
1002 Platal::page()->killError("Type de recherche non valide.");
1003 }
1004 }
1005
1006 public function buildCondition(PlFilter $uf)
1007 {
1008 $sub = $uf->addJobCompanyFilter();
1009 $cond = $sub . '.' . $this->type . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->value);
1010 $jsub = $uf->addJobFilter();
1011 $cond .= ' AND ' . $uf->getVisibilityCondition($jsub . '.pub');
1012 return $cond;
1013 }
1014 }
1015 // }}}
1016
1017 // {{{ class UFC_Job_Terms
1018 /** Filters users based on the job terms they assigned to one of their
1019 * jobs.
1020 * @param $val The ID of the job term, or an array of such IDs
1021 */
1022 class UFC_Job_Terms extends UserFilterCondition
1023 {
1024 private $val;
1025
1026 public function __construct($val)
1027 {
1028 if (!is_array($val)) {
1029 $val = array($val);
1030 }
1031 $this->val = $val;
1032 }
1033
1034 public function buildCondition(PlFilter $uf)
1035 {
1036 $sub = $uf->addJobTermsFilter(count($this->val));
1037 $conditions = array();
1038 foreach ($this->val as $i => $jtid) {
1039 $conditions[] = $sub[$i] . '.jtid_1 = ' . XDB::escape($jtid);
1040 }
1041 $jsub = $uf->addJobFilter();
1042 $conditions[] = $uf->getVisibilityCondition($jsub . '.pub');
1043 return implode(' AND ', $conditions);
1044 }
1045 }
1046 // }}}
1047
1048 // {{{ class UFC_Job_Description
1049 /** Filters users based on their job description
1050 * @param $description The text being searched for
1051 * @param $fields The fields to search for (CV, user-defined)
1052 */
1053 class UFC_Job_Description extends UserFilterCondition
1054 {
1055
1056 private $description;
1057 private $fields;
1058
1059 public function __construct($description, $fields)
1060 {
1061 $this->fields = $fields;
1062 $this->description = $description;
1063 }
1064
1065 public function buildCondition(PlFilter $uf)
1066 {
1067 $conds = array();
1068
1069 $jsub = $uf->addJobFilter();
1070 // CV is private => if only CV requested, and not private,
1071 // don't do anything. Otherwise restrict to standard job visibility.
1072 if ($this->fields == UserFilter::JOB_CV) {
1073 if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) {
1074 return self::CONF_TRUE;
1075 }
1076 } else {
1077 $conds[] = $uf->getVisibilityCondition($jsub . '.pub');
1078 }
1079
1080 if ($this->fields & UserFilter::JOB_USERDEFINED) {
1081 $conds[] = $jsub . '.description ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description);
1082 }
1083 if ($this->fields & UserFilter::JOB_CV && $uf->getVisibilityLevel() == ProfileVisibility::VIS_PRIVATE) {
1084 $uf->requireProfiles();
1085 $conds[] = 'p.cv ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description);
1086 }
1087 return implode(' OR ', $conds);
1088 }
1089 }
1090 // }}}
1091
1092 // {{{ class UFC_Networking
1093 /** Filters users based on network identity (IRC, ...)
1094 * @param $type Type of network (-1 for any)
1095 * @param $value Value to search
1096 */
1097 class UFC_Networking extends UserFilterCondition
1098 {
1099 private $type;
1100 private $value;
1101
1102 public function __construct($type, $value)
1103 {
1104 $this->type = $type;
1105 $this->value = $value;
1106 }
1107
1108 public function buildCondition(PlFilter $uf)
1109 {
1110 $sub = $uf->addNetworkingFilter();
1111 $conds = array();
1112 $conds[] = $uf->getVisibilityCondition($sub . '.pub');
1113 $conds[] = $sub . '.address ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->value);
1114 if ($this->type != -1) {
1115 $conds[] = $sub . '.nwid = ' . XDB::format('{?}', $this->type);
1116 }
1117 return implode(' AND ', $conds);
1118 }
1119 }
1120 // }}}
1121
1122 // {{{ class UFC_Phone
1123 /** Filters users based on their phone number
1124 * @param $num_type Type of number (pro/user/home)
1125 * @param $phone_type Type of phone (fixed/mobile/fax)
1126 * @param $number Phone number
1127 */
1128 class UFC_Phone extends UserFilterCondition
1129 {
1130 const NUM_PRO = 'pro';
1131 const NUM_USER = 'user';
1132 const NUM_HOME = 'address';
1133 const NUM_ANY = 'any';
1134
1135 const PHONE_FIXED = 'fixed';
1136 const PHONE_MOBILE = 'mobile';
1137 const PHONE_FAX = 'fax';
1138 const PHONE_ANY = 'any';
1139
1140 private $num_type;
1141 private $phone_type;
1142 private $number;
1143
1144 public function __construct($number, $num_type = self::NUM_ANY, $phone_type = self::PHONE_ANY)
1145 {
1146 $phone = new Phone(array('display' => $number));
1147 $phone->format();
1148 $this->number = $phone->search();
1149 $this->num_type = $num_type;
1150 $this->phone_type = $phone_type;
1151 }
1152
1153 public function buildCondition(PlFilter $uf)
1154 {
1155 $sub = $uf->addPhoneFilter();
1156 $conds = array();
1157
1158 $conds[] = $uf->getVisibilityCondition($sub . '.pub');
1159
1160 $conds[] = $sub . '.search_tel = ' . XDB::format('{?}', $this->number);
1161 if ($this->num_type != self::NUM_ANY) {
1162 $conds[] = $sub . '.link_type = ' . XDB::format('{?}', $this->num_type);
1163 }
1164 if ($this->phone_type != self::PHONE_ANY) {
1165 $conds[] = $sub . '.tel_type = ' . XDB::format('{?}', $this->phone_type);
1166 }
1167 return implode(' AND ', $conds);
1168 }
1169 }
1170 // }}}
1171
1172 // {{{ class UFC_Medal
1173 /** Filters users based on their medals
1174 * @param $medal ID of the medal
1175 * @param $grade Grade of the medal (null for 'any')
1176 */
1177 class UFC_Medal extends UserFilterCondition
1178 {
1179 private $medal;
1180 private $grade;
1181
1182 public function __construct($medal, $grade = null)
1183 {
1184 $this->medal = $medal;
1185 $this->grade = $grade;
1186 }
1187
1188 public function buildCondition(PlFilter $uf)
1189 {
1190 $conds = array();
1191
1192 // This will require profiles => table 'p' will be available.
1193 $sub = $uf->addMedalFilter();
1194
1195 $conds[] = $uf->getVisibilityCondition('p.medals_pub');
1196
1197 $conds[] = $sub . '.mid = ' . XDB::format('{?}', $this->medal);
1198 if ($this->grade != null) {
1199 $conds[] = $sub . '.gid = ' . XDB::format('{?}', $this->grade);
1200 }
1201 return implode(' AND ', $conds);
1202 }
1203 }
1204 // }}}
1205
1206 // {{{ class UFC_Photo
1207 /** Filters profiles with photo
1208 */
1209 class UFC_Photo extends UserFilterCondition
1210 {
1211 public function buildCondition(PlFilter $uf)
1212 {
1213 $sub = $uf->addPhotoFilter();
1214 return $sub . '.attach IS NOT NULL AND ' . $uf->getVisibilityCondition($sub . '.pub');
1215 }
1216 }
1217 // }}}
1218
1219 // {{{ class UFC_Mentor
1220 class UFC_Mentor extends UserFilterCondition
1221 {
1222 public function buildCondition(PlFilter $uf)
1223 {
1224 $sub = $uf->addMentorFilter(UserFilter::MENTOR);
1225 return $sub . '.expertise IS NOT NULL';
1226 }
1227 }
1228 // }}}
1229
1230
1231 // {{{ class UFC_Mentor_Expertise
1232 /** Filters users by mentoring expertise
1233 * @param $expertise Domain of expertise
1234 */
1235 class UFC_Mentor_Expertise extends UserFilterCondition
1236 {
1237 private $expertise;
1238
1239 public function __construct($expertise)
1240 {
1241 $this->expertise = $expertise;
1242 }
1243
1244 public function buildCondition(PlFilter $uf)
1245 {
1246 $sub = $uf->addMentorFilter(UserFilter::MENTOR_EXPERTISE);
1247 return $sub . '.expertise ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->expertise);
1248 }
1249 }
1250 // }}}
1251
1252 // {{{ class UFC_Mentor_Country
1253 /** Filters users by mentoring country
1254 * @param $country Two-letters code of country being searched
1255 */
1256 class UFC_Mentor_Country extends UserFilterCondition
1257 {
1258 private $country;
1259
1260 public function __construct()
1261 {
1262 $this->country = pl_flatten(func_get_args());
1263 }
1264
1265 public function buildCondition(PlFilter $uf)
1266 {
1267 $sub = $uf->addMentorFilter(UserFilter::MENTOR_COUNTRY);
1268 return $sub . '.country IN ' . XDB::format('{?}', $this->country);
1269 }
1270 }
1271 // }}}
1272
1273 // {{{ class UFC_Mentor_Terms
1274 /** Filters users based on the job terms they used in mentoring.
1275 * @param $val The ID of the job term, or an array of such IDs
1276 */
1277 class UFC_Mentor_Terms extends UserFilterCondition
1278 {
1279 private $val;
1280
1281 public function __construct($val)
1282 {
1283 $this->val = $val;
1284 }
1285
1286 public function buildCondition(PlFilter $uf)
1287 {
1288 $sub = $uf->addMentorFilter(UserFilter::MENTOR_TERM);
1289 return $sub . '.jtid_1 = ' . XDB::escape($this->val);
1290 }
1291 }
1292 // }}}
1293
1294 // {{{ class UFC_UserRelated
1295 /** Filters users based on a relation toward a user
1296 * @param $user User to which searched users are related
1297 */
1298 abstract class UFC_UserRelated extends UserFilterCondition
1299 {
1300 protected $user;
1301 public function __construct(PlUser &$user)
1302 {
1303 $this->user =& $user;
1304 }
1305 }
1306 // }}}
1307
1308 // {{{ class UFC_Contact
1309 /** Filters users who belong to selected user's contacts
1310 */
1311 class UFC_Contact extends UFC_UserRelated
1312 {
1313 public function buildCondition(PlFilter $uf)
1314 {
1315 $sub = $uf->addContactFilter($this->user->id());
1316 return 'c' . $sub . '.contact IS NOT NULL';
1317 }
1318 }
1319 // }}}
1320
1321 // {{{ class UFC_WatchRegistration
1322 /** Filters users being watched by selected user
1323 */
1324 class UFC_WatchRegistration extends UFC_UserRelated
1325 {
1326 public function buildCondition(PlFilter $uf)
1327 {
1328 if (!$this->user->watchType('registration')) {
1329 return PlFilterCondition::COND_FALSE;
1330 }
1331 $uids = $this->user->watchUsers();
1332 if (count($uids) == 0) {
1333 return PlFilterCondition::COND_FALSE;
1334 } else {
1335 return XDB::format('$UID IN {?}', $uids);
1336 }
1337 }
1338 }
1339 // }}}
1340
1341 // {{{ class UFC_WatchPromo
1342 /** Filters users belonging to a promo watched by selected user
1343 * @param $user Selected user (the one watching promo)
1344 * @param $grade Formation the user is watching
1345 */
1346 class UFC_WatchPromo extends UFC_UserRelated
1347 {
1348 private $grade;
1349 public function __construct(PlUser &$user, $grade = UserFilter::GRADE_ING)
1350 {
1351 parent::__construct($user);
1352 $this->grade = $grade;
1353 }
1354
1355 public function buildCondition(PlFilter $uf)
1356 {
1357 $promos = $this->user->watchPromos();
1358 if (count($promos) == 0) {
1359 return PlFilterCondition::COND_FALSE;
1360 } else {
1361 $sube = $uf->addEducationFilter(true, $this->grade);
1362 $field = 'pe' . $sube . '.' . UserFilter::promoYear($this->grade);
1363 return XDB::format($field . ' IN {?}', $promos);
1364 }
1365 }
1366 }
1367 // }}}
1368
1369 // {{{ class UFC_WatchContact
1370 /** Filters users watched by selected user
1371 */
1372 class UFC_WatchContact extends UFC_Contact
1373 {
1374 public function buildCondition(PlFilter $uf)
1375 {
1376 if (!$this->user->watchContacts()) {
1377 return PlFilterCondition::COND_FALSE;
1378 }
1379 return parent::buildCondition($uf);
1380 }
1381 }
1382 // }}}
1383
1384 // {{{ class UFC_MarketingHash
1385 /** Filters users using the hash generated
1386 * to send marketing emails to him.
1387 */
1388 class UFC_MarketingHash extends UserFilterCondition
1389 {
1390 private $hash;
1391
1392 public function __construct($hash)
1393 {
1394 $this->hash = $hash;
1395 }
1396
1397 public function buildCondition(PlFilter $uf)
1398 {
1399 $table = $uf->addMarketingHash();
1400 return XDB::format('rm.hash = {?}', $this->hash);
1401 }
1402 }
1403 // }}}
1404
1405 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
1406 ?>