551d3acd4b4341aed00b63473b67965071812b43
2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
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. *
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. *
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 *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
27 // {{{ interface UserFilterCondition
28 /** This interface describe objects which filter users based
29 * on various parameters.
30 * The parameters of the filter must be given to the constructor.
31 * The buildCondition function is called by UserFilter when
32 * actually building the query. That function must call
33 * $uf->addWheteverFilter so that the UserFilter makes
34 * adequate joins. It must return the 'WHERE' condition to use
37 interface UserFilterCondition
extends PlFilterCondition
42 // {{{ class UFC_HasProfile
43 /** Filters users who have a profile
45 class UFC_HasProfile
implements UserFilterCondition
47 public function buildCondition(PlFilter
&$uf)
49 $uf->requireProfiles();
50 return 'p.pid IS NOT NULL';
55 // {{{ class UFC_Hruid
56 /** Filters users based on their hruid
57 * @param $val Either an hruid, or a list of those
59 class UFC_Hruid
implements UserFilterCondition
63 public function __construct($val)
65 if (!is_array($val)) {
71 public function buildCondition(PlFilter
&$uf)
73 $uf->requireAccounts();
74 return XDB
::format('a.hruid IN {?}', $this->hruids
);
79 // {{{ class UFC_Hrpid
80 /** Filters users based on the hrpid of their profiles
81 * @param $val Either an hrpid, or a list of those
83 class UFC_Hrpid
implements UserFilterCondition
87 public function __construct($val)
89 if (!is_array($val)) {
95 public function buildCondition(PlFilter
&$uf)
97 $uf->requireProfiles();
98 return XDB
::format('p.hrpid IN {?}', $this->hrpids
);
104 /** Filters users based on one of their last IPs
105 * @param $ip IP from which connection are checked
107 class UFC_Ip
implements UserFilterCondition
111 public function __construct($ip)
116 public function buildCondition(PlFilter
&$uf)
118 $sub = $uf->addLoggerFilter();
119 $ip = ip_to_uint($this->ip
);
120 return XDB
::format($sub . '.ip = {?} OR ' . $sub . '.forward_ip = {?}', $ip, $ip);
125 // {{{ class UFC_Comment
126 class UFC_Comment
implements UserFilterCondition
130 public function __construct($text)
135 public function buildCondition(PlFilter
&$uf)
137 $uf->requireProfiles();
138 return 'p.freetext ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->text
);
143 // {{{ class UFC_Promo
144 /** Filters users based on promotion
145 * @param $comparison Comparison operator (>, =, ...)
146 * @param $grade Formation on which to restrict, UserFilter::DISPLAY for "any formation"
147 * @param $promo Promotion on which the filter is based
149 class UFC_Promo
implements UserFilterCondition
156 public function __construct($comparison, $grade, $promo)
158 $this->grade
= $grade;
159 $this->comparison
= $comparison;
160 $this->promo
= $promo;
161 if ($this->grade
!= UserFilter
::DISPLAY
) {
162 UserFilter
::assertGrade($this->grade
);
166 public function buildCondition(PlFilter
&$uf)
168 if ($this->grade
== UserFilter
::DISPLAY
) {
169 $sub = $uf->addDisplayFilter();
170 return XDB
::format('pd' . $sub . '.promo ' . $this->comparison
. ' {?}', $this->promo
);
172 $sub = $uf->addEducationFilter(true
, $this->grade
);
173 $field = 'pe' . $sub . '.' . UserFilter
::promoYear($this->grade
);
174 return $field . ' IS NOT NULL AND ' . $field . ' ' . $this->comparison
. ' ' . XDB
::format('{?}', $this->promo
);
180 // {{{ class UFC_SchoolId
181 /** Filters users based on their shoold identifier
182 * @param type Parameter type (Xorg, AX, School)
183 * @param value School id value
185 class UFC_SchoolId
implements UserFilterCondition
189 const School
= 'school';
194 static public function assertType($type)
196 if ($type != self
::AX
&& $type != self
::Xorg
&& $type != self
::School
) {
197 Platal
::page()->killError("Type de matricule invalide: $type");
201 public function __construct($type, $id)
205 self
::assertType($type);
208 public function buildCondition(PlFilter
&$uf)
210 $uf->requireProfiles();
213 if ($type == self
::School
) {
215 $id = Profile
::getXorgId($id);
217 return XDB
::format('p.' . $type . '_id = {?}', $id);
222 // {{{ class UFC_EducationSchool
223 /** Filters users by formation
224 * @param $val The formation to search (either ID or array of IDs)
226 class UFC_EducationSchool
implements UserFilterCondition
230 public function __construct($val)
232 if (!is_array($val)) {
238 public function buildCondition(PlFilter
&$uf)
240 $sub = $uf->addEducationFilter();
241 return XDB
::format('pe' . $sub . '.eduid IN {?}', $this->val
);
246 // {{{ class UFC_EducationDegree
247 class UFC_EducationDegree
implements UserFilterCondition
251 public function __construct($diploma)
253 if (! is_array($diploma)) {
254 $diploma = array($diploma);
256 $this->diploma
= $diploma;
259 public function buildCondition(PlFilter
&$uf)
261 $sub = $uf->addEducationFilter();
262 return XDB
::format('pe' . $sub . '.degreeid IN {?}', $this->diploma
);
267 // {{{ class UFC_EducationField
268 class UFC_EducationField
implements UserFilterCondition
272 public function __construct($val)
274 if (!is_array($val)) {
280 public function buildCondition(PlFilter
&$uf)
282 $sub = $uf->addEducationFilter();
283 return XDB
::format('pe' . $sub . '.fieldid IN {?}', $this->val
);
288 // {{{ class UFC_Name
289 /** Filters users based on name
290 * @param $type Type of name field on which filtering is done (firstname, lastname...)
291 * @param $text Text on which to filter
292 * @param $mode Flag indicating search type (prefix, suffix, with particule...)
294 class UFC_Name
implements UserFilterCondition
296 const PREFIX
= XDB
::WILDCARD_PREFIX
; // 0x001
297 const SUFFIX
= XDB
::WILDCARD_SUFFIX
; // 0x002
298 const CONTAINS
= XDB
::WILDCARD_CONTAINS
; // 0x003
299 const PARTICLE
= 0x007; // self::CONTAINS | 0x004
300 const VARIANTS
= 0x008;
306 public function __construct($type, $text, $mode)
313 private function buildNameQuery($type, $variant, $where, UserFilter
&$uf)
315 $sub = $uf->addNameFilter($type, $variant);
316 return str_replace('$ME', 'pn' . $sub, $where);
319 public function buildCondition(PlFilter
&$uf)
322 if (($this->mode
& self
::PARTICLE
) == self
::PARTICLE
) {
323 $left = 'CONCAT($ME.particle, \' \', $ME.name)';
325 $right = XDB
::formatWildcards($this->mode
& self
::CONTAINS
, $this->text
);
327 $cond = $left . $right;
328 $conds = array($this->buildNameQuery($this->type
, null
, $cond, $uf));
329 if (($this->mode
& self
::VARIANTS
) != 0 && isset(Profile
::$name_variants[$this->type
])) {
330 foreach (Profile
::$name_variants[$this->type
] as $var) {
331 $conds[] = $this->buildNameQuery($this->type
, $var, $cond, $uf);
334 return implode(' OR ', $conds);
339 // {{{ class UFC_NameTokens
340 /** Selects users based on tokens in their name (for quicksearch)
341 * @param $tokens An array of tokens to search
342 * @param $flags Flags the tokens must have (e.g 'public' for public search)
343 * @param $soundex (bool) Whether those tokens are fulltext or soundex
345 class UFC_NameTokens
implements UserFilterCondition
348 const FLAG_PUBLIC
= 'public';
355 public function __construct($tokens, $flags = array(), $soundex = false
, $exact = false
)
357 $this->tokens
= $tokens;
358 if (is_array($flags)) {
359 $this->flags
= $flags;
361 $this->flags
= array($flags);
363 $this->soundex
= $soundex;
364 $this->exact
= $exact;
367 public function buildCondition(PlFilter
&$uf)
369 $sub = $uf->addNameTokensFilter(!($this->exact ||
$this->soundex
));
371 if ($this->soundex
) {
372 $conds[] = XDB
::format($sub . '.soundex IN {?}', $this->tokens
);
373 } else if ($this->exact
) {
374 $conds[] = XDB
::format($sub . '.token IN {?}', $this->tokens
);
377 foreach ($this->tokens
as $token) {
378 $tokconds[] = $sub . '.token ' . XDB
::formatWildcards(XDB
::WILDCARD_PREFIX
, $token);
380 $conds[] = implode(' OR ', $tokconds);
383 if ($this->flags
!= null
) {
384 $conds[] = XDB
::format($sub . '.flags IN {?}', $this->flags
);
387 return implode(' AND ', $conds);
392 // {{{ class UFC_Nationality
393 class UFC_Nationality
implements UserFilterCondition
397 public function __construct($val)
399 if (!is_array($val)) {
405 public function buildCondition(PlFilter
&$uf)
407 $uf->requireProfiles();
408 $nat = XDB
::formatArray($this->val
);
410 'p.nationality1 IN ' . $nat,
411 'p.nationality2 IN ' . $nat,
412 'p.nationality3 IN ' . $nat,
414 return implode(' OR ', $conds);
419 // {{{ class UFC_Dead
420 /** Filters users based on death date
421 * @param $comparison Comparison operator
422 * @param $date Date to which death date should be compared
424 class UFC_Dead
implements UserFilterCondition
429 public function __construct($comparison = null
, $date = null
)
431 $this->comparison
= $comparison;
435 public function buildCondition(PlFilter
&$uf)
437 $uf->requireProfiles();
438 $str = 'p.deathdate IS NOT NULL';
439 if (!is_null($this->comparison
)) {
440 $str .= ' AND p.deathdate ' . $this->comparison
. ' ' . XDB
::format('{?}', date('Y-m-d', $this->date
));
447 // {{{ class UFC_Registered
448 /** Filters users based on registration state
449 * @param $active Whether we want to use only "active" users (i.e with a valid redirection)
450 * @param $comparison Comparison operator
451 * @param $date Date to which users registration date should be compared
453 class UFC_Registered
implements UserFilterCondition
459 public function __construct($active = false
, $comparison = null
, $date = null
)
461 $this->active
= $active;
462 $this->comparison
= $comparison;
466 public function buildCondition(PlFilter
&$uf)
468 $uf->requireAccounts();
470 $date = 'a.uid IS NOT NULL AND a.state = \'active\'';
472 $date = 'a.uid IS NOT NULL AND a.state != \'pending\'';
474 if (!is_null($this->comparison
)) {
475 $date .= ' AND a.registration_date ' . $this->comparison
. ' ' . XDB
::format('{?}', date('Y-m-d', $this->date
));
482 // {{{ class UFC_ProfileUpdated
483 /** Filters users based on profile update date
484 * @param $comparison Comparison operator
485 * @param $date Date to which profile update date must be compared
487 class UFC_ProfileUpdated
implements UserFilterCondition
492 public function __construct($comparison = null
, $date = null
)
494 $this->comparison
= $comparison;
498 public function buildCondition(PlFilter
&$uf)
500 $uf->requireProfiles();
501 return 'p.last_change ' . $this->comparison
. XDB
::format(' {?}', date('Y-m-d H:i:s', $this->date
));
506 // {{{ class UFC_Birthday
507 /** Filters users based on next birthday date
508 * @param $comparison Comparison operator
509 * @param $date Date to which users next birthday date should be compared
511 class UFC_Birthday
implements UserFilterCondition
516 public function __construct($comparison = null
, $date = null
)
518 $this->comparison
= $comparison;
522 public function buildCondition(PlFilter
&$uf)
524 $uf->requireProfiles();
525 return 'p.next_birthday ' . $this->comparison
. XDB
::format(' {?}', date('Y-m-d', $this->date
));
531 /** Filters users based on sex
532 * @parm $sex One of User::GENDER_MALE or User::GENDER_FEMALE, for selecting users
534 class UFC_Sex
implements UserFilterCondition
537 public function __construct($sex)
542 public function buildCondition(PlFilter
&$uf)
544 if ($this->sex
!= User
::GENDER_MALE
&& $this->sex
!= User
::GENDER_FEMALE
) {
545 return self
::COND_FALSE
;
547 $uf->requireProfiles();
548 return XDB
::format('p.sex = {?}', $this->sex
== User
::GENDER_FEMALE ?
'female' : 'male');
554 // {{{ class UFC_Group
555 /** Filters users based on group membership
556 * @param $group Group whose members we are selecting
557 * @param $anim Whether to restrict selection to animators of that group
559 class UFC_Group
implements UserFilterCondition
563 public function __construct($group, $anim = false
)
565 $this->group
= $group;
569 public function buildCondition(PlFilter
&$uf)
571 $sub = $uf->addGroupFilter($this->group
);
572 $where = 'gpm' . $sub . '.perms IS NOT NULL';
574 $where .= ' AND gpm' . $sub . '.perms = \'admin\'';
581 // {{{ class UFC_Binet
582 /** Selects users based on their belonging to a given (list of) binet
583 * @param $binet either a binet_id or an array of binet_ids
585 class UFC_Binet
implements UserFilterCondition
589 public function __construct($val)
591 if (!is_array($val)) {
597 public function buildCondition(PlFilter
&$uf)
599 $sub = $uf->addBinetsFilter();
600 return XDB
::format($sub . '.binet_id IN {?}', $this->val
);
605 // {{{ class UFC_Section
606 /** Selects users based on section
607 * @param $section ID of the section
609 class UFC_Section
implements UserFilterCondition
613 public function __construct($section)
615 $this->section
= $section;
618 public function buildCondition(PlFilter
&$uf)
620 $uf->requireProfiles();
621 return 'p.section = ' . XDB
::format('{?}', $this->section
);
626 // {{{ class UFC_Email
627 /** Filters users based on an email or a list of emails
628 * @param $emails List of emails whose owner must be selected
630 class UFC_Email
implements UserFilterCondition
633 public function __construct()
635 $this->emails
= func_get_args();
638 public function buildCondition(PlFilter
&$uf)
645 if (count($this->emails
) == 0) {
646 return PlFilterCondition
::COND_TRUE
;
649 foreach ($this->emails
as $entry) {
650 if (User
::isForeignEmailAddress($entry)) {
652 } else if (User
::isVirtualEmailAddress($entry)) {
655 @list
($user, $domain) = explode('@', $entry);
660 if (count($foreign) > 0) {
661 $sub = $uf->addEmailRedirectFilter($foreign);
662 $cond[] = XDB
::format('e' . $sub . '.email IS NOT NULL OR a.email IN {?}', $foreign);
664 if (count($virtual) > 0) {
665 $sub = $uf->addVirtualEmailFilter($virtual);
666 $cond[] = 'vr' . $sub . '.redirect IS NOT NULL';
668 if (count($aliases) > 0) {
669 $sub = $uf->addAliasFilter($aliases);
670 $cond[] = 'al' . $sub . '.alias IS NOT NULL';
672 return '(' . implode(') OR (', $cond) . ')';
677 // {{{ class UFC_Address
678 abstract class UFC_Address
implements UserFilterCondition
680 /** Valid address type ('hq' is reserved for company addresses)
686 /** Text for these types
688 protected static $typetexts = array(
689 self
::TYPE_HOME
=> 'home',
690 self
::TYPE_PRO
=> 'pro',
695 /** Flags for addresses
697 const FLAG_CURRENT
= 0x0001;
698 const FLAG_TEMP
= 0x0002;
699 const FLAG_SECOND
= 0x0004;
700 const FLAG_MAIL
= 0x0008;
701 const FLAG_CEDEX
= 0x0010;
703 // Binary OR of those flags
704 const FLAG_ANY
= 0x001F;
706 /** Text of these flags
708 protected static $flagtexts = array(
709 self
::FLAG_CURRENT
=> 'current',
710 self
::FLAG_TEMP
=> 'temporary',
711 self
::FLAG_SECOND
=> 'secondary',
712 self
::FLAG_MAIL
=> 'mail',
713 self
::FLAG_CEDEX
=> 'cedex',
718 public function __construct($type = null
, $flags = null
)
721 $this->flags
= $flags;
724 protected function initConds($sub)
728 foreach (self
::$typetexts as $flag => $type) {
729 if ($flag & $this->type
) {
734 $conds[] = XDB
::format($sub . '.type IN {?}', $types);
737 if ($this->flags
!= self
::FLAG_ANY
) {
738 foreach(self
::$flagtexts as $flag => $text) {
739 if ($flag & $this->flags
) {
740 $conds[] = 'FIND_IN_SET(' . XDB
::format('{?}', $text) . ', ' . $sub . '.flags)';
750 // {{{ class UFC_AddressText
751 /** Select users based on their address, using full text search
752 * @param $text Text for filter in fulltext search
753 * @param $textSearchMode Mode for search (one of XDB::WILDCARD_*)
754 * @param $type Filter on address type
755 * @param $flags Filter on address flags
756 * @param $country Filter on address country
757 * @param $locality Filter on address locality
759 class UFC_AddressText
extends UFC_Address
763 private $textSearchMode;
765 public function __construct($text = null
, $textSearchMode = XDB
::WILDCARD_CONTAINS
,
766 $type = null
, $flags = self
::FLAG_ANY
, $country = null
, $locality = null
)
768 parent
::__construct($type, $flags);
770 $this->textSearchMode
= $textSearchMode;
771 $this->country
= $country;
772 $this->locality
= $locality;
775 private function mkMatch($txt)
777 return XDB
::formatWildcards($this->textSearchMode
, $txt);
780 public function buildCondition(PlFilter
&$uf)
782 $sub = $uf->addAddressFilter();
783 $conds = $this->initConds($sub);
784 if ($this->text
!= null
) {
785 $conds[] = $sub . '.text' . $this->mkMatch($this->text
);
788 if ($this->country
!= null
) {
789 $subc = $uf->addAddressCountryFilter();
791 $subconds[] = $subc . '.country' . $this->mkMatch($this->country
);
792 $subconds[] = $subc . '.countryFR' . $this->mkMatch($this->country
);
793 $conds[] = implode(' OR ', $subconds);
796 if ($this->locality
!= null
) {
797 $subl = $uf->addAddressLocalityFilter();
798 $conds[] = $subl . '.name' . $this->mkMatch($this->locality
);
801 return implode(' AND ', $conds);
806 // {{{ class UFC_AddressField
807 /** Filters users based on their address,
808 * @param $val Either a code for one of the fields, or an array of such codes
809 * @param $fieldtype The type of field to look for
810 * @param $type Filter on address type
811 * @param $flags Filter on address flags
813 class UFC_AddressField
extends UFC_Address
815 const FIELD_COUNTRY
= 1;
816 const FIELD_ADMAREA
= 2;
817 const FIELD_SUBADMAREA
= 3;
818 const FIELD_LOCALITY
= 4;
819 const FIELD_ZIPCODE
= 5;
821 /** Data of the filter
826 public function __construct($val, $fieldtype, $type = null
, $flags = self
::FLAG_ANY
)
828 parent
::__construct($type, $flags);
830 if (!is_array($val)) {
834 $this->fieldtype
= $fieldtype;
837 public function buildCondition(PlFilter
&$uf)
839 $sub = $uf->addAddressFilter();
840 $conds = $this->initConds($sub);
842 switch ($this->fieldtype
) {
843 case self
::FIELD_COUNTRY
:
844 $field = 'countryId';
846 case self
::FIELD_ADMAREA
:
847 $field = 'administrativeAreaId';
849 case self
::FIELD_SUBADMAREA
:
850 $field = 'subAdministrativeAreaId';
852 case self
::FIELD_LOCALITY
:
853 $field = 'localityId';
855 case self
::FIELD_ZIPCODE
:
856 $field = 'postalCode';
859 Platal
::page()->killError('Invalid address field type: ' . $this->fieldtype
);
861 $conds[] = XDB
::format($sub . '.' . $field . ' IN {?}', $this->val
);
863 return implode(' AND ', $conds);
868 // {{{ class UFC_Corps
869 /** Filters users based on the corps they belong to
870 * @param $corps Corps we are looking for (abbreviation)
871 * @param $type Whether we search for original or current corps
873 class UFC_Corps
implements UserFilterCondition
881 public function __construct($corps, $type = self
::CURRENT
)
883 $this->corps
= $corps;
887 public function buildCondition(PlFilter
&$uf)
889 /** Tables shortcuts:
890 * pc for profile_corps,
891 * pceo for profile_corps_enum - orginal
892 * pcec for profile_corps_enum - current
894 $sub = $uf->addCorpsFilter($this->type
);
895 $cond = $sub . '.abbreviation = ' . $corps;
901 // {{{ class UFC_Corps_Rank
902 /** Filters users based on their rank in the corps
903 * @param $rank Rank we are looking for (abbreviation)
905 class UFC_Corps_Rank
implements UserFilterCondition
908 public function __construct($rank)
913 public function buildCondition(PlFilter
&$uf)
915 /** Tables shortcuts:
916 * pcr for profile_corps_rank
918 $sub = $uf->addCorpsRankFilter();
919 $cond = $sub . '.abbreviation = ' . $rank;
925 // {{{ class UFC_Job_Company
926 /** Filters users based on the company they belong to
927 * @param $type The field being searched (self::JOBID, self::JOBNAME or self::JOBACRONYM)
928 * @param $value The searched value
930 class UFC_Job_Company
implements UserFilterCondition
933 const JOBNAME
= 'name';
934 const JOBACRONYM
= 'acronym';
939 public function __construct($type, $value)
941 $this->assertType($type);
943 $this->value
= $value;
946 private function assertType($type)
948 if ($type != self
::JOBID
&& $type != self
::JOBNAME
&& $type != self
::JOBACRONYM
) {
949 Platal
::page()->killError("Type de recherche non valide.");
953 public function buildCondition(PlFilter
&$uf)
955 $sub = $uf->addJobCompanyFilter();
956 $cond = $sub . '.' . $this->type
. ' = ' . XDB
::format('{?}', $this->value
);
962 // {{{ class UFC_Job_Sectorization
963 /** Filters users based on the ((sub)sub)sector they work in
964 * @param $val The ID of the sector, or an array of such IDs
965 * @param $type The kind of search (subsubsector/subsector/sector)
967 class UFC_Job_Sectorization
implements UserFilterCondition
972 public function __construct($val, $type = UserFilter
::JOB_SECTOR
)
974 self
::assertType($type);
975 if (!is_array($val)) {
982 private static function assertType($type)
984 if ($type != UserFilter
::JOB_SECTOR
&& $type != UserFilter
::JOB_SUBSECTOR
&& $type != UserFilter
::JOB_SUBSUBSECTOR
) {
985 Platal
::page()->killError("Type de secteur non valide.");
989 public function buildCondition(PlFilter
&$uf)
991 $sub = $uf->addJobSectorizationFilter($this->type
);
992 return $sub . '.id = ' . XDB
::format('{?}', $this->val
);
997 // {{{ class UFC_Job_Description
998 /** Filters users based on their job description
999 * @param $description The text being searched for
1000 * @param $fields The fields to search for (user-defined, ((sub|)sub|)sector)
1002 class UFC_Job_Description
implements UserFilterCondition
1005 private $description;
1008 public function __construct($description, $fields)
1010 $this->fields
= $fields;
1011 $this->description
= $description;
1014 public function buildCondition(PlFilter
&$uf)
1017 if ($this->fields
& UserFilter
::JOB_USERDEFINED
) {
1018 $sub = $uf->addJobFilter();
1019 $conds[] = $sub . '.description ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->description
);
1021 if ($this->fields
& UserFilter
::JOB_CV
) {
1022 $uf->requireProfiles();
1023 $conds[] = 'p.cv ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->description
);
1025 if ($this->fields
& UserFilter
::JOB_SECTOR
) {
1026 $sub = $uf->addJobSectorizationFilter(UserFilter
::JOB_SECTOR
);
1027 $conds[] = $sub . '.name ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->description
);
1029 if ($this->fields
& UserFilter
::JOB_SUBSECTOR
) {
1030 $sub = $uf->addJobSectorizationFilter(UserFilter
::JOB_SUBSECTOR
);
1031 $conds[] = $sub . '.name ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->description
);
1033 if ($this->fields
& UserFilter
::JOB_SUBSUBSECTOR
) {
1034 $sub = $uf->addJobSectorizationFilter(UserFilter
::JOB_SUBSUBSECTOR
);
1035 $conds[] = $sub . '.name ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->description
);
1036 $sub = $uf->addJobSectorizationFilter(UserFilter
::JOB_ALTERNATES
);
1037 $conds[] = $sub . '.name ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->description
);
1039 return implode(' OR ', $conds);
1044 // {{{ class UFC_Networking
1045 /** Filters users based on network identity (IRC, ...)
1046 * @param $type Type of network (-1 for any)
1047 * @param $value Value to search
1049 class UFC_Networking
implements UserFilterCondition
1054 public function __construct($type, $value)
1056 $this->type
= $type;
1057 $this->value
= $value;
1060 public function buildCondition(PlFilter
&$uf)
1062 $sub = $uf->addNetworkingFilter();
1064 $conds[] = $sub . '.address ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->value
);
1065 if ($this->type
!= -1) {
1066 $conds[] = $sub . '.network_type = ' . XDB
::format('{?}', $this->type
);
1068 return implode(' AND ', $conds);
1073 // {{{ class UFC_Phone
1074 /** Filters users based on their phone number
1075 * @param $num_type Type of number (pro/user/home)
1076 * @param $phone_type Type of phone (fixed/mobile/fax)
1077 * @param $number Phone number
1079 class UFC_Phone
implements UserFilterCondition
1081 const NUM_PRO
= 'pro';
1082 const NUM_USER
= 'user';
1083 const NUM_HOME
= 'address';
1084 const NUM_ANY
= 'any';
1086 const PHONE_FIXED
= 'fixed';
1087 const PHONE_MOBILE
= 'mobile';
1088 const PHONE_FAX
= 'fax';
1089 const PHONE_ANY
= 'any';
1092 private $phone_type;
1095 public function __construct($number, $num_type = self
::NUM_ANY
, $phone_type = self
::PHONE_ANY
)
1097 require_once('profil.func.inc.php');
1098 $this->number
= $number;
1099 $this->num_type
= $num_type;
1100 $this->phone_type
= format_phone_number($phone_type);
1103 public function buildCondition(PlFilter
&$uf)
1105 $sub = $uf->addPhoneFilter();
1107 $conds[] = $sub . '.search_tel = ' . XDB
::format('{?}', $this->number
);
1108 if ($this->num_type
!= self
::NUM_ANY
) {
1109 $conds[] = $sub . '.link_type = ' . XDB
::format('{?}', $this->num_type
);
1111 if ($this->phone_type
!= self
::PHONE_ANY
) {
1112 $conds[] = $sub . '.tel_type = ' . XDB
::format('{?}', $this->phone_type
);
1114 return implode(' AND ', $conds);
1119 // {{{ class UFC_Medal
1120 /** Filters users based on their medals
1121 * @param $medal ID of the medal
1122 * @param $grade Grade of the medal (null for 'any')
1124 class UFC_Medal
implements UserFilterCondition
1129 public function __construct($medal, $grade = null
)
1131 $this->medal
= $medal;
1132 $this->grade
= $grade;
1135 public function buildCondition(PlFilter
&$uf)
1138 $sub = $uf->addMedalFilter();
1139 $conds[] = $sub . '.mid = ' . XDB
::format('{?}', $this->medal
);
1140 if ($this->grade
!= null
) {
1141 $conds[] = $sub . '.gid = ' . XDB
::format('{?}', $this->grade
);
1143 return implode(' AND ', $conds);
1148 // {{{ class UFC_Photo
1149 /** Filters profiles with photo
1151 class UFC_Photo
implements UserFilterCondition
1153 public function buildCondition(PlFilter
&$uf)
1155 $uf->addPhotoFilter();
1156 return 'photo.attach IS NOT NULL';
1161 // {{{ class UFC_Mentor_Expertise
1162 /** Filters users by mentoring expertise
1163 * @param $expertise Domain of expertise
1165 class UFC_Mentor_Expertise
implements UserFilterCondition
1169 public function __construct($expertise)
1171 $this->expertise
= $expertise;
1174 public function buildCondition(PlFilter
&$uf)
1176 $sub = $uf->addMentorFilter(UserFilter
::MENTOR_EXPERTISE
);
1177 return $sub . '.expertise ' . XDB
::formatWildcards(XDB
::WILDCARD_CONTAINS
, $this->expertise
);
1182 // {{{ class UFC_Mentor_Country
1183 /** Filters users by mentoring country
1184 * @param $country Two-letters code of country being searched
1186 class UFC_Mentor_Country
implements UserFilterCondition
1190 public function __construct($country)
1192 $this->country
= $country;
1195 public function buildCondition(PlFilter
&$uf)
1197 $sub = $uf->addMentorFilter(UserFilter
::MENTOR_COUNTRY
);
1198 return $sub . '.country = ' . XDB
::format('{?}', $this->country
);
1203 // {{{ class UFC_Mentor_Sectorization
1204 /** Filters users based on mentoring (sub|)sector
1205 * @param $sector ID of (sub)sector
1206 * @param $type Whether we are looking for a sector or a subsector
1208 class UFC_Mentor_Sectorization
implements UserFilterCondition
1211 const SUBSECTOR
= 2;
1215 public function __construct($sector, $type = self
::SECTOR
)
1217 $this->sector
= $sector;
1218 $this->type
= $type;
1221 public function buildCondition(PlFilter
&$uf)
1223 $sub = $uf->addMentorFilter(UserFilter
::MENTOR_SECTOR
);
1224 if ($this->type
== self
::SECTOR
) {
1225 $field = 'sectorid';
1227 $field = 'subsectorid';
1229 return $sub . '.' . $field . ' = ' . XDB
::format('{?}', $this->sector
);
1234 // {{{ class UFC_UserRelated
1235 /** Filters users based on a relation toward a user
1236 * @param $user User to which searched users are related
1238 abstract class UFC_UserRelated
implements UserFilterCondition
1241 public function __construct(PlUser
&$user)
1243 $this->user
=& $user;
1248 // {{{ class UFC_Contact
1249 /** Filters users who belong to selected user's contacts
1251 class UFC_Contact
extends UFC_UserRelated
1253 public function buildCondition(PlFilter
&$uf)
1255 $sub = $uf->addContactFilter($this->user
->id());
1256 return 'c' . $sub . '.contact IS NOT NULL';
1261 // {{{ class UFC_WatchRegistration
1262 /** Filters users being watched by selected user
1264 class UFC_WatchRegistration
extends UFC_UserRelated
1266 public function buildCondition(PlFilter
&$uf)
1268 if (!$this->user
->watchType('registration')) {
1269 return PlFilterCondition
::COND_FALSE
;
1271 $uids = $this->user
->watchUsers();
1272 if (count($uids) == 0) {
1273 return PlFilterCondition
::COND_FALSE
;
1275 return XDB
::format('$UID IN {?}', $uids);
1281 // {{{ class UFC_WatchPromo
1282 /** Filters users belonging to a promo watched by selected user
1283 * @param $user Selected user (the one watching promo)
1284 * @param $grade Formation the user is watching
1286 class UFC_WatchPromo
extends UFC_UserRelated
1289 public function __construct(PlUser
&$user, $grade = UserFilter
::GRADE_ING
)
1291 parent
::__construct($user);
1292 $this->grade
= $grade;
1295 public function buildCondition(PlFilter
&$uf)
1297 $promos = $this->user
->watchPromos();
1298 if (count($promos) == 0) {
1299 return PlFilterCondition
::COND_FALSE
;
1301 $sube = $uf->addEducationFilter(true
, $this->grade
);
1302 $field = 'pe' . $sube . '.' . UserFilter
::promoYear($this->grade
);
1303 return XDB
::format($field . ' IN {?}', $promos);
1309 // {{{ class UFC_WatchContact
1310 /** Filters users watched by selected user
1312 class UFC_WatchContact
extends UFC_Contact
1314 public function buildCondition(PlFilter
&$uf)
1316 if (!$this->user
->watchContacts()) {
1317 return PlFilterCondition
::COND_FALSE
;
1319 return parent
::buildCondition($uf);
1324 // {{{ class UFC_MarketingHash
1325 /** Filters users using the hash generated
1326 * to send marketing emails to him.
1328 class UFC_MarketingHash
implements UserFilterCondition
1332 public function __construct($hash)
1334 $this->hash
= $hash;
1337 public function buildCondition(PlFilter
&$uf)
1339 $table = $uf->addMarketingHash();
1340 return XDB
::format('rm.hash = {?}', $this->hash
);
1349 // {{{ class UserFilterOrder
1350 /** Base class for ordering results of a query.
1351 * Parameters for the ordering must be given to the constructor ($desc for a
1352 * descending order).
1353 * The getSortTokens function is used to get actual ordering part of the query.
1355 abstract class UserFilterOrder
extends PlFilterOrder
1357 /** This function must return the tokens to use for ordering
1358 * @param &$uf The UserFilter whose results must be ordered
1359 * @return The name of the field to use for ordering results
1361 // abstract protected function getSortTokens(UserFilter &$uf);
1365 // {{{ class UFO_Promo
1366 /** Orders users by promotion
1367 * @param $grade Formation whose promotion users should be sorted by (restricts results to users of that formation)
1368 * @param $desc Whether sort is descending
1370 class UFO_Promo
extends UserFilterOrder
1374 public function __construct($grade = null
, $desc = false
)
1376 parent
::__construct($desc);
1377 $this->grade
= $grade;
1380 protected function getSortTokens(PlFilter
&$uf)
1382 if (UserFilter
::isGrade($this->grade
)) {
1383 $sub = $uf->addEducationFilter($this->grade
);
1384 return 'pe' . $sub . '.' . UserFilter
::promoYear($this->grade
);
1386 $sub = $uf->addDisplayFilter();
1387 return 'pd' . $sub . '.promo';
1393 // {{{ class UFO_Name
1394 /** Sorts users by name
1395 * @param $type Type of name on which to sort (firstname...)
1396 * @param $variant Variant of that name to use (marital, ordinary...)
1397 * @param $particle Set to true if particles should be included in the sorting order
1398 * @param $desc If sort order should be descending
1400 class UFO_Name
extends UserFilterOrder
1406 public function __construct($type, $variant = null
, $particle = false
, $desc = false
)
1408 parent
::__construct($desc);
1409 $this->type
= $type;
1410 $this->variant
= $variant;
1411 $this->particle
= $particle;
1414 protected function getSortTokens(PlFilter
&$uf)
1416 if (Profile
::isDisplayName($this->type
)) {
1417 $sub = $uf->addDisplayFilter();
1418 return 'pd' . $sub . '.' . $this->type
;
1420 $sub = $uf->addNameFilter($this->type
, $this->variant
);
1421 if ($this->particle
) {
1422 return 'CONCAT(pn' . $sub . '.particle, \' \', pn' . $sub . '.name)';
1424 return 'pn' . $sub . '.name';
1431 // {{{ class UFO_Score
1432 class UFO_Score
extends UserFilterOrder
1434 protected function getSortTokens(PlFilter
&$uf)
1436 $sub = $uf->addNameTokensFilter();
1437 return 'SUM(' . $sub . '.score)';
1442 // {{{ class UFO_Registration
1443 /** Sorts users based on registration date
1445 class UFO_Registration
extends UserFilterOrder
1447 protected function getSortTokens(PlFilter
&$uf)
1449 return 'a.registration_date';
1454 // {{{ class UFO_Birthday
1455 /** Sorts users based on next birthday date
1457 class UFO_Birthday
extends UserFilterOrder
1459 protected function getSortTokens(PlFilter
&$uf)
1461 return 'p.next_birthday';
1466 // {{{ class UFO_ProfileUpdate
1467 /** Sorts users based on last profile update
1469 class UFO_ProfileUpdate
extends UserFilterOrder
1471 protected function getSortTokens(PlFilter
&$uf)
1473 return 'p.last_change';
1478 // {{{ class UFO_Death
1479 /** Sorts users based on death date
1481 class UFO_Death
extends UserFilterOrder
1483 protected function getSortTokens(PlFilter
&$uf)
1485 return 'p.deathdate';
1491 /***********************************
1492 *********************************
1494 *********************************
1495 ***********************************/
1497 // {{{ class UserFilter
1498 /** This class provides a convenient and centralized way of filtering users.
1501 * $uf = new UserFilter(new UFC_Blah($x, $y), new UFO_Coin($z, $t));
1503 * Resulting UserFilter can be used to:
1504 * - get a list of User objects matching the filter
1505 * - get a list of UIDs matching the filter
1506 * - get the number of users matching the filter
1507 * - check whether a given User matches the filter
1508 * - filter a list of User objects depending on whether they match the filter
1510 * Usage for UFC and UFO objects:
1511 * A UserFilter will call all private functions named XXXJoins.
1512 * These functions must return an array containing the list of join
1513 * required by the various UFC and UFO associated to the UserFilter.
1514 * Entries in those returned array are of the following form:
1515 * 'join_tablealias' => array('join_type', 'joined_table', 'join_criter')
1516 * which will be translated into :
1517 * join_type JOIN joined_table AS join_tablealias ON (join_criter)
1518 * in the final query.
1520 * In the join_criter text, $ME is replaced with 'join_tablealias', $PID with
1521 * profile.pid, and $UID with accounts.uid.
1523 * For each kind of "JOIN" needed, a function named addXXXFilter() should be defined;
1524 * its parameter will be used to set various private vars of the UserFilter describing
1525 * the required joins ; such a function shall return the "join_tablealias" to use
1526 * when referring to the joined table.
1528 * For example, if data from profile_job must be available to filter results,
1529 * the UFC object will call $uf-addJobFilter(), which will set the 'with_pj' var and
1530 * return 'pj', the short name to use when referring to profile_job; when building
1531 * the query, calling the jobJoins function will return an array containing a single
1533 * 'pj' => array('left', 'profile_job', '$ME.pid = $UID');
1535 * The 'register_optional' function can be used to generate unique table aliases when
1536 * the same table has to be joined several times with different aliases.
1538 class UserFilter
extends PlFilter
1540 protected $joinMethods = array();
1542 protected $joinMetas = array(
1548 private $sort = array();
1549 private $query = null
;
1550 private $orderby = null
;
1552 private $lastusercount = null
;
1553 private $lastprofilecount = null
;
1555 public function __construct($cond = null
, $sort = null
)
1557 if (empty($this->joinMethods
)) {
1558 $class = new ReflectionClass('UserFilter');
1559 foreach ($class->getMethods() as $method) {
1560 $name = $method->getName();
1561 if (substr($name, -5) == 'Joins' && $name != 'buildJoins') {
1562 $this->joinMethods
[] = $name;
1566 if (!is_null($cond)) {
1567 if ($cond instanceof PlFilterCondition
) {
1568 $this->setCondition($cond);
1571 if (!is_null($sort)) {
1572 if ($sort instanceof UserFilterOrder
) {
1573 $this->addSort($sort);
1574 } else if (is_array($sort)) {
1575 foreach ($sort as $s) {
1582 private function buildQuery()
1584 if (is_null($this->orderby
)) {
1586 foreach ($this->sort
as $sort) {
1587 $orders = array_merge($orders, $sort->buildSort($this));
1589 if (count($orders) == 0) {
1590 $this->orderby
= '';
1592 $this->orderby
= 'ORDER BY ' . implode(', ', $orders);
1595 if (is_null($this->query
)) {
1596 $where = $this->root
->buildCondition($this);
1597 if ($this->with_forced_sn
) {
1598 $this->requireProfiles();
1599 $from = 'search_name AS sn';
1600 } else if ($this->with_accounts
) {
1601 $from = 'accounts AS a';
1603 $this->requireProfiles();
1604 $from = 'profiles AS p';
1606 $joins = $this->buildJoins();
1607 $this->query
= 'FROM ' . $from . '
1609 WHERE (' . $where . ')';
1613 private function getUIDList($uids = null
, PlLimit
&$limit)
1615 $this->requireAccounts();
1616 $this->buildQuery();
1617 $lim = $limit->getSql();
1619 if (!is_null($uids)) {
1620 $cond = XDB
::format(' AND a.uid IN {?}', $uids);
1622 $fetched = XDB
::fetchColumn('SELECT SQL_CALC_FOUND_ROWS a.uid
1623 ' . $this->query
. $cond . '
1625 ' . $this->orderby
. '
1627 $this->lastusercount
= (int)XDB
::fetchOneCell('SELECT FOUND_ROWS()');
1631 private function getPIDList($pids = null
, PlLimit
&$limit)
1633 $this->requireProfiles();
1634 $this->buildQuery();
1635 $lim = $limit->getSql();
1637 if (!is_null($pids)) {
1638 $cond = XDB
::format(' AND p.pid IN {?}', $pids);
1640 $fetched = XDB
::fetchColumn('SELECT SQL_CALC_FOUND_ROWS p.pid
1641 ' . $this->query
. $cond . '
1643 ' . $this->orderby
. '
1645 $this->lastprofilecount
= (int)XDB
::fetchOneCell('SELECT FOUND_ROWS()');
1649 private static function defaultLimit($limit) {
1650 if ($limit == null
) {
1651 return new PlLimit();
1657 /** Check that the user match the given rule.
1659 public function checkUser(PlUser
&$user)
1661 $this->requireAccounts();
1662 $this->buildQuery();
1663 $count = (int)XDB
::fetchOneCell('SELECT COUNT(*)
1664 ' . $this->query
. XDB
::format(' AND a.uid = {?}', $user->id()));
1668 /** Check that the profile match the given rule.
1670 public function checkProfile(Profile
&$profile)
1672 $this->requireProfiles();
1673 $this->buildQuery();
1674 $count = (int)XDB
::fetchOneCell('SELECT COUNT(*)
1675 ' . $this->query
. XDB
::format(' AND p.pid = {?}', $profile->id()));
1679 /** Default filter is on users
1681 public function filter(array $users, $limit = null
)
1683 return $this->filterUsers($users, self
::defaultLimit($limit));
1686 /** Filter a list of users to extract the users matching the rule.
1688 public function filterUsers(array $users, $limit = null
)
1690 $limit = self
::defaultLimit($limit);
1691 $this->requireAccounts();
1692 $this->buildQuery();
1695 foreach ($users as $user) {
1696 if ($user instanceof PlUser
) {
1702 $table[$uid] = $user;
1704 $fetched = $this->getUIDList($uids, $limit);
1706 foreach ($fetched as $uid) {
1707 $output[] = $table[$uid];
1712 /** Filter a list of profiles to extract the users matching the rule.
1714 public function filterProfiles(array $profiles, $limit = null
)
1716 $limit = self
::defaultLimit($limit);
1717 $this->requireProfiles();
1718 $this->buildQuery();
1721 foreach ($profiles as $profile) {
1722 if ($profile instanceof Profile
) {
1723 $pid = $profile->id();
1728 $table[$pid] = $profile;
1730 $fetched = $this->getPIDList($pids, $limit);
1732 foreach ($fetched as $pid) {
1733 $output[] = $table[$pid];
1738 public function getUIDs($limit = null
)
1740 $limit = self
::defaultLimit($limit);
1741 return $this->getUIDList(null
, $limit);
1744 public function getUID($pos = 0)
1746 $uids =$this->getUIDList(null
, new PlFilter(1, $pos));
1747 if (count($uids) == 0) {
1754 public function getPIDs($limit = null
)
1756 $limit = self
::defaultLimit($limit);
1757 return $this->getPIDList(null
, $limit);
1760 public function getPID($pos = 0)
1762 $pids =$this->getPIDList(null
, new PlFilter(1, $pos));
1763 if (count($pids) == 0) {
1770 public function getUsers($limit = null
)
1772 return User
::getBulkUsersWithUIDs($this->getUIDs($limit));
1775 public function getUser($pos = 0)
1777 $uid = $this->getUID($pos);
1781 return User
::getWithUID($uid);
1785 public function iterUsers($limit = null
)
1787 return User
::iterOverUIDs($this->getUIDs($limit));
1790 public function getProfiles($limit = null
, $fields = 0x0000, $visibility = null
)
1792 return Profile
::getBulkProfilesWithPIDs($this->getPIDs($limit), $fields, $visibility);
1795 public function getProfile($pos = 0, $fields = 0x0000, $visibility = null
)
1797 $pid = $this->getPID($pos);
1801 return Profile
::get($pid, $fields, $visibility);
1805 public function iterProfiles($limit = null
, $fields = 0x0000, $visibility = null
)
1807 return Profile
::iterOverPIDs($this->getPIDs($limit), true
, $fields, $visibility);
1810 public function get($limit = null
)
1812 return $this->getUsers($limit);
1816 public function getTotalCount()
1818 return $this->getTotalUserCount();
1821 public function getTotalUserCount()
1823 if (is_null($this->lastusercount
)) {
1824 $this->requireAccounts();
1825 $this->buildQuery();
1826 return (int)XDB
::fetchOneCell('SELECT COUNT(DISTINCT a.uid)
1829 return $this->lastusercount
;
1833 public function getTotalProfileCount()
1835 if (is_null($this->lastprofilecount
)) {
1836 $this->requireProfiles();
1837 $this->buildQuery();
1838 return (int)XDB
::fetchOneCell('SELECT COUNT(DISTINCT p.pid)
1841 return $this->lastprofilecount
;
1845 public function setCondition(PlFilterCondition
&$cond)
1847 $this->root
=& $cond;
1848 $this->query
= null
;
1851 public function addSort(PlFilterOrder
&$sort)
1853 $this->sort
[] = $sort;
1854 $this->orderby
= null
;
1857 static public function getLegacy($promo_min, $promo_max)
1859 if ($promo_min != 0) {
1860 $min = new UFC_Promo('>=', self
::GRADE_ING
, intval($promo_min));
1862 $min = new PFC_True();
1864 if ($promo_max != 0) {
1865 $max = new UFC_Promo('<=', self
::GRADE_ING
, intval($promo_max));
1867 $max = new PFC_True();
1869 return new UserFilter(new PFC_And($min, $max));
1872 static public function sortByName()
1874 return array(new UFO_Name(Profile
::LASTNAME
), new UFO_Name(Profile
::FIRSTNAME
));
1877 static public function sortByPromo()
1879 return array(new UFO_Promo(), new UFO_Name(Profile
::LASTNAME
), new UFO_Name(Profile
::FIRSTNAME
));
1882 static private function getDBSuffix($string)
1884 if (is_array($string)) {
1885 if (count($string) == 1) {
1886 return self
::getDBSuffix(array_pop($string));
1888 return md5(implode('|', $string));
1890 return preg_replace('/[^a-z0-9]/i', '', $string);
1895 /** Stores a new (and unique) table alias in the &$table table
1896 * @param &$table Array in which the table alias must be stored
1897 * @param $val Value which will then be used to build the join
1898 * @return Name of the newly created alias
1900 private $option = 0;
1901 private function register_optional(array &$table, $val)
1903 if (is_null($val)) {
1904 $sub = $this->option++
;
1907 $sub = self
::getDBSuffix($val);
1911 $table[$sub] = $index;
1915 /** PROFILE VS ACCOUNT
1917 private $with_profiles = false
;
1918 private $with_accounts = false
;
1919 private $with_forced_sn = false
;
1920 public function requireAccounts()
1922 $this->with_accounts
= true
;
1925 public function requireProfiles()
1927 $this->with_profiles
= true
;
1930 /** Forces the "FROM" to use search_name instead of accounts or profiles */
1931 public function forceSearchName()
1933 $this->with_forced_sn
= true
;
1936 protected function accountJoins()
1939 /** Quick search is much more efficient with sn first and PID second */
1940 if ($this->with_forced_sn
) {
1941 $joins['p'] = PlSqlJoin
::left('profiles', '$PID = sn.pid');
1942 if ($this->with_accounts
) {
1943 $joins['ap'] = PlSqlJoin
::left('account_profiles', '$ME.pid = $PID');
1944 $joins['a'] = PlSqlJoin
::left('accounts', '$UID = ap.uid');
1946 } else if ($this->with_profiles
&& $this->with_accounts
) {
1947 $joins['ap'] = PlSqlJoin
::left('account_profiles', '$ME.uid = $UID AND FIND_IN_SET(\'owner\', ap.perms)');
1948 $joins['p'] = PlSqlJoin
::left('profiles', '$PID = ap.pid');
1955 const DISPLAY
= 'display';
1956 private $pd = false
;
1957 public function addDisplayFilter()
1959 $this->requireProfiles();
1964 protected function displayJoins()
1967 return array('pd' => PlSqlJoin
::left('profile_display', '$ME.pid = $PID'));
1976 private $with_logger = false
;
1977 public function addLoggerFilter()
1979 $this->with_logger
= true
;
1980 $this->requireAccounts();
1983 protected function loggerJoins()
1986 if ($this->with_logger
) {
1987 $joins['ls'] = PlSqlJoin
::left('log_sessions', '$ME.uid = $UID');
1995 static public function assertName($name)
1997 if (!DirEnum
::getID(DirEnum
::NAMETYPES
, $name)) {
1998 Platal
::page()->kill('Invalid name type: ' . $name);
2002 private $pn = array();
2003 public function addNameFilter($type, $variant = null
)
2005 $this->requireProfiles();
2006 if (!is_null($variant)) {
2007 $ft = $type . '_' . $variant;
2012 self
::assertName($ft);
2014 if (!is_null($variant) && $variant == 'other') {
2015 $sub .= $this->option++
;
2017 $this->pn
[$sub] = DirEnum
::getID(DirEnum
::NAMETYPES
, $ft);
2021 protected function nameJoins()
2024 foreach ($this->pn
as $sub => $type) {
2025 $joins['pn' . $sub] = PlSqlJoin
::left('profile_name', '$ME.pid = $PID AND $ME.typeid = {?}', $type);
2032 private $with_sn = false
;
2033 // Set $doingQuickSearch to true if you wish to optimize the query
2034 public function addNameTokensFilter($doingQuickSearch = false
)
2036 $this->requireProfiles();
2037 $this->with_forced_sn
= ($this->with_forced_sn ||
$doingQuickSearch);
2038 $this->with_sn
= true
;
2042 protected function nameTokensJoins()
2044 /* We don't return joins, since with_sn forces the SELECT to run on search_name first */
2045 if ($this->with_sn
&& !$this->with_forced_sn
) {
2047 'sn' => PlSqlJoin
::left('search_name', '$ME.pid = $PID')
2057 private $with_nat = false
;
2058 public function addNationalityFilter()
2060 $this->with_nat
= true
;
2064 protected function nationalityJoins()
2067 if ($this->with_nat
) {
2068 $joins['ngc'] = PlSqlJoin
::left('geoloc_countries', '$ME.iso_3166_1_a2 = p.nationality1 OR $ME.iso_3166_1_a2 = p.nationality2 OR $ME.iso_3166_1_a2 = p.nationality3');
2075 const GRADE_ING
= 'Ing.';
2076 const GRADE_PHD
= 'PhD';
2077 const GRADE_MST
= 'M%';
2078 static public function isGrade($grade)
2080 return ($grade !== 0) && ($grade == self
::GRADE_ING ||
$grade == self
::GRADE_PHD ||
$grade == self
::GRADE_MST
);
2083 static public function assertGrade($grade)
2085 if (!self
::isGrade($grade)) {
2086 Platal
::page()->killError("Diplôme non valide: $grade");
2090 static public function promoYear($grade)
2092 // XXX: Definition of promotion for phds and masters might change in near future.
2093 return ($grade == UserFilter
::GRADE_ING
) ?
'entry_year' : 'grad_year';
2096 private $pepe = array();
2097 private $with_pee = false
;
2098 public function addEducationFilter($x = false
, $grade = null
)
2100 $this->requireProfiles();
2102 $index = $this->option
;
2103 $sub = $this->option++
;
2105 self
::assertGrade($grade);
2108 $this->with_pee
= true
;
2111 $this->pepe
[$index] = $sub;
2115 protected function educationJoins()
2118 if ($this->with_pee
) {
2119 $joins['pee'] = PlSqlJoin
::inner('profile_education_enum', 'pee.abbreviation = \'X\'');
2121 foreach ($this->pepe
as $grade => $sub) {
2122 if ($this->isGrade($grade)) {
2123 $joins['pe' . $sub] = PlSqlJoin
::left('profile_education', '$ME.eduid = pee.id AND $ME.pid = $PID');
2124 $joins['pede' . $sub] = PlSqlJoin
::inner('profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid AND $ME.abbreviation LIKE {?}', $grade);
2126 $joins['pe' . $sub] = PlSqlJoin
::left('profile_education', '$ME.pid = $PID');
2127 $joins['pee' . $sub] = PlSqlJoin
::inner('profile_education_enum', '$ME.id = pe' . $sub . '.eduid');
2128 $joins['pede' . $sub] = PlSqlJoin
::inner('profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid');
2137 private $gpm = array();
2138 public function addGroupFilter($group = null
)
2140 $this->requireAccounts();
2141 if (!is_null($group)) {
2142 if (is_int($group) ||
ctype_digit($group)) {
2143 $index = $sub = $group;
2146 $sub = self
::getDBSuffix($group);
2149 $sub = 'group_' . $this->option++
;
2153 $this->gpm
[$sub] = $index;
2157 protected function groupJoins()
2160 foreach ($this->gpm
as $sub => $key) {
2161 if (is_null($key)) {
2162 $joins['gpa' . $sub] = PlSqlJoin
::inner('groups');
2163 $joins['gpm' . $sub] = PlSqlJoin
::left('group_members', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id');
2164 } else if (is_int($key) ||
ctype_digit($key)) {
2165 $joins['gpm' . $sub] = PlSqlJoin
::left('group_members', '$ME.uid = $UID AND $ME.asso_id = ' . $key);
2167 $joins['gpa' . $sub] = PlSqlJoin
::inner('groups', '$ME.diminutif = {?}', $key);
2168 $joins['gpm' . $sub] = PlSqlJoin
::left('group_members', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id');
2177 private $with_bi = false
;
2178 private $with_bd = false
;
2179 public function addBinetsFilter($with_enum = false
)
2181 $this->requireProfiles();
2182 $this->with_bi
= true
;
2184 $this->with_bd
= true
;
2191 protected function binetsJoins()
2194 if ($this->with_bi
) {
2195 $joins['bi'] = PlSqlJoin
::left('profile_binets', '$ME.pid = $PID');
2197 if ($this->with_bd
) {
2198 $joins['bd'] = PlSqlJoin
::left('profile_binet_enum', '$ME.id = bi.binet_id');
2205 private $e = array();
2206 public function addEmailRedirectFilter($email = null
)
2208 $this->requireAccounts();
2209 return $this->register_optional($this->e
, $email);
2212 private $ve = array();
2213 public function addVirtualEmailFilter($email = null
)
2215 $this->addAliasFilter(self
::ALIAS_FORLIFE
);
2216 return $this->register_optional($this->ve
, $email);
2219 const ALIAS_BEST
= 'bestalias';
2220 const ALIAS_FORLIFE
= 'forlife';
2221 private $al = array();
2222 public function addAliasFilter($alias = null
)
2224 $this->requireAccounts();
2225 return $this->register_optional($this->al
, $alias);
2228 protected function emailJoins()
2232 foreach ($this->e
as $sub=>$key) {
2233 if (is_null($key)) {
2234 $joins['e' . $sub] = PlSqlJoin
::left('emails', '$ME.uid = $UID AND $ME.flags != \'filter\'');
2236 if (!is_array($key)) {
2239 $joins['e' . $sub] = PlSqlJoin
::left('emails', '$ME.uid = $UID AND $ME.flags != \'filter\'
2240 AND $ME.email IN {?}' . $key);
2243 foreach ($this->al
as $sub=>$key) {
2244 if (is_null($key)) {
2245 $joins['al' . $sub] = PlSqlJoin
::left('aliases', '$ME.uid = $UID AND $ME.type IN (\'alias\', \'a_vie\')');
2246 } else if ($key == self
::ALIAS_BEST
) {
2247 $joins['al' . $sub] = PlSqlJoin
::left('aliases', '$ME.uid = $UID AND $ME.type IN (\'alias\', \'a_vie\') AND FIND_IN_SET(\'bestalias\', $ME.flags)');
2248 } else if ($key == self
::ALIAS_FORLIFE
) {
2249 $joins['al' . $sub] = PlSqlJoin
::left('aliases', '$ME.uid = $UID AND $ME.type = \'a_vie\'');
2251 if (!is_array($key)) {
2254 $joins['al' . $sub] = PlSqlJoin
::left('aliases', '$ME.uid = $UID AND $ME.type IN (\'alias\', \'a_vie\')
2255 AND $ME.alias IN {?}', $key);
2258 foreach ($this->ve
as $sub=>$key) {
2259 if (is_null($key)) {
2260 $joins['v' . $sub] = PlSqlJoin
::left('virtual', '$ME.type = \'user\'');
2262 if (!is_array($key)) {
2265 $joins['v' . $sub] = PlSqlJoin
::left('virtual', '$ME.type = \'user\' AND $ME.alias IN {?}', $key);
2267 $joins['vr' . $sub] = PlSqlJoin
::left('virtual_redirect',
2268 '$ME.vid = v' . $sub . '.vid
2269 AND ($ME.redirect IN (CONCAT(al_forlife.alias, \'@\', {?}),
2270 CONCAT(al_forlife.alias, \'@\', {?}),
2272 $globals->mail
->domain
, $globals->mail
->domain2
);
2280 private $with_pa = false
;
2281 public function addAddressFilter()
2283 $this->requireProfiles();
2284 $this->with_pa
= true
;
2288 private $with_pac = false
;
2289 public function addAddressCountryFilter()
2291 $this->requireProfiles();
2292 $this->addAddressFilter();
2293 $this->with_pac
= true
;
2297 private $with_pal = false
;
2298 public function addAddressLocalityFilter()
2300 $this->requireProfiles();
2301 $this->addAddressFilter();
2302 $this->with_pal
= true
;
2306 protected function addressJoins()
2309 if ($this->with_pa
) {
2310 $joins['pa'] = PlSqlJoin
::left('profile_addresses', '$ME.pid = $PID');
2312 if ($this->with_pac
) {
2313 $joins['gc'] = PlSqlJoin
::left('geoloc_countries', '$ME.iso_3166_1_a2 = pa.countryID');
2315 if ($this->with_pal
) {
2316 $joins['gl'] = PlSqlJoin
::left('geoloc_localities', '$ME.id = pa.localityID');
2325 private $pc = false
;
2326 private $pce = array();
2327 private $pcr = false
;
2328 public function addCorpsFilter($type)
2330 $this->requireProfiles();
2332 if ($type == UFC_Corps
::CURRENT
) {
2333 $pce['pcec'] = 'current_corpsid';
2335 } else if ($type == UFC_Corps
::ORIGIN
) {
2336 $pce['pceo'] = 'original_corpsid';
2341 public function addCorpsRankFilter()
2343 $this->requireProfiles();
2349 protected function corpsJoins()
2353 $joins['pc'] = PlSqlJoin
::left('profile_corps', '$ME.pid = $PID');
2356 $joins['pcr'] = PlSqlJoin
::left('profile_corps_rank_enum', '$ME.id = pc.rankid');
2358 foreach($this->pce
as $sub => $field) {
2359 $joins[$sub] = PlSqlJoin
::left('profile_corps_enum', '$ME.id = pc.' . $field);
2367 const JOB_SECTOR
= 0x0001;
2368 const JOB_SUBSECTOR
= 0x0002;
2369 const JOB_SUBSUBSECTOR
= 0x0004;
2370 const JOB_ALTERNATES
= 0x0008;
2371 const JOB_USERDEFINED
= 0x0010;
2372 const JOB_CV
= 0x0020;
2374 const JOB_SECTORIZATION
= 0x000F;
2375 const JOB_ANY
= 0x003F;
2379 * pje => profile_job_enum
2380 * pjse => profile_job_sector_enum
2381 * pjsse => profile_job_subsector_enum
2382 * pjssse => profile_job_subsubsector_enum
2383 * pja => profile_job_alternates
2385 private $with_pj = false
;
2386 private $with_pje = false
;
2387 private $with_pjse = false
;
2388 private $with_pjsse = false
;
2389 private $with_pjssse = false
;
2390 private $with_pja = false
;
2392 public function addJobFilter()
2394 $this->requireProfiles();
2395 $this->with_pj
= true
;
2399 public function addJobCompanyFilter()
2401 $this->addJobFilter();
2402 $this->with_pje
= true
;
2406 public function addJobSectorizationFilter($type)
2408 $this->addJobFilter();
2409 if ($type == self
::JOB_SECTOR
) {
2410 $this->with_pjse
= true
;
2412 } else if ($type == self
::JOB_SUBSECTOR
) {
2413 $this->with_pjsse
= true
;
2415 } else if ($type == self
::JOB_SUBSUBSECTOR
) {
2416 $this->with_pjssse
= true
;
2418 } else if ($type == self
::JOB_ALTERNATES
) {
2419 $this->with_pja
= true
;
2424 protected function jobJoins()
2427 if ($this->with_pj
) {
2428 $joins['pj'] = PlSqlJoin
::left('profile_job', '$ME.pid = $PID');
2430 if ($this->with_pje
) {
2431 $joins['pje'] = PlSqlJoin
::left('profile_job_enum', '$ME.id = pj.jobid');
2433 if ($this->with_pjse
) {
2434 $joins['pjse'] = PlSqlJoin
::left('profile_job_sector_enum', '$ME.id = pj.sectorid');
2436 if ($this->with_pjsse
) {
2437 $joins['pjsse'] = PlSqlJoin
::left('profile_job_subsector_enum', '$ME.id = pj.subsectorid');
2439 if ($this->with_pjssse
) {
2440 $joins['pjssse'] = PlSqlJoin
::left('profile_job_subsubsector_enum', '$ME.id = pj.subsubsectorid');
2442 if ($this->with_pja
) {
2443 $joins['pja'] = PlSqlJoin
::left('profile_job_alternates', '$ME.subsubsectorid = pj.subsubsectorid');
2451 private $with_pnw = false
;
2452 public function addNetworkingFilter()
2454 $this->requireAccounts();
2455 $this->with_pnw
= true
;
2459 protected function networkingJoins()
2462 if ($this->with_pnw
) {
2463 $joins['pnw'] = PlSqlJoin
::left('profile_networking', '$ME.pid = $PID');
2471 private $with_ptel = false
;
2473 public function addPhoneFilter()
2475 $this->requireAccounts();
2476 $this->with_ptel
= true
;
2480 protected function phoneJoins()
2483 if ($this->with_ptel
) {
2484 $joins['ptel'] = PlSqlJoin
::left('profile_phones', '$ME.pid = $PID');
2492 private $with_pmed = false
;
2493 public function addMedalFilter()
2495 $this->requireProfiles();
2496 $this->with_pmed
= true
;
2500 protected function medalJoins()
2503 if ($this->with_pmed
) {
2504 $joins['pmed'] = PlSqlJoin
::left('profile_medals', '$ME.pid = $PID');
2512 private $pms = array();
2513 const MENTOR_EXPERTISE
= 1;
2514 const MENTOR_COUNTRY
= 2;
2515 const MENTOR_SECTOR
= 3;
2517 public function addMentorFilter($type)
2519 $this->requireAccounts();
2521 case self
::MENTOR_EXPERTISE
:
2522 $this->pms
['pme'] = 'profile_mentor';
2524 case self
::MENTOR_COUNTRY
:
2525 $this->pms
['pmc'] = 'profile_mentor_country';
2527 case self
::MENTOR_SECTOR
:
2528 $this->pms
['pms'] = 'profile_mentor_sector';
2531 Platal
::page()->killError("Undefined mentor filter.");
2535 protected function mentorJoins()
2538 foreach ($this->pms
as $sub => $tab) {
2539 $joins[$sub] = PlSqlJoin
::left($tab, '$ME.pid = $PID');
2546 private $cts = array();
2547 public function addContactFilter($uid = null
)
2549 $this->requireProfiles();
2550 return $this->register_optional($this->cts
, is_null($uid) ? null
: 'user_' . $uid);
2553 protected function contactJoins()
2556 foreach ($this->cts
as $sub=>$key) {
2557 if (is_null($key)) {
2558 $joins['c' . $sub] = PlSqlJoin
::left('contacts', '$ME.contact = $PID');
2560 $joins['c' . $sub] = PlSqlJoin
::left('contacts', '$ME.uid = {?} AND $ME.contact = $PID', substr($key, 5));
2569 private $wn = array();
2570 public function addWatchRegistrationFilter($uid = null
)
2572 $this->requireAccounts();
2573 return $this->register_optional($this->wn
, is_null($uid) ? null
: 'user_' . $uid);
2576 private $wp = array();
2577 public function addWatchPromoFilter($uid = null
)
2579 $this->requireAccounts();
2580 return $this->register_optional($this->wp
, is_null($uid) ? null
: 'user_' . $uid);
2583 private $w = array();
2584 public function addWatchFilter($uid = null
)
2586 $this->requireAccounts();
2587 return $this->register_optional($this->w
, is_null($uid) ? null
: 'user_' . $uid);
2590 protected function watchJoins()
2593 foreach ($this->w
as $sub=>$key) {
2594 if (is_null($key)) {
2595 $joins['w' . $sub] = PlSqlJoin
::left('watch');
2597 $joins['w' . $sub] = PlSqlJoin
::left('watch', '$ME.uid = {?}', substr($key, 5));
2600 foreach ($this->wn
as $sub=>$key) {
2601 if (is_null($key)) {
2602 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.ni_id = $UID');
2604 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5));
2607 foreach ($this->wn
as $sub=>$key) {
2608 if (is_null($key)) {
2609 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.ni_id = $UID');
2611 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5));
2614 foreach ($this->wp
as $sub=>$key) {
2615 if (is_null($key)) {
2616 $joins['wp' . $sub] = PlSqlJoin
::left('watch_promo');
2618 $joins['wp' . $sub] = PlSqlJoin
::left('watch_promo', '$ME.uid = {?}', substr($key, 5));
2627 private $with_photo;
2628 public function addPhotoFilter()
2630 $this->requireProfiles();
2631 $this->with_photo
= true
;
2634 protected function photoJoins()
2636 if ($this->with_photo
) {
2637 return array('photo' => PlSqlJoin
::left('profile_photos', '$ME.pid = $PID'));
2647 public function addMarketingHash()
2649 $this->requireAccounts();
2650 $this->with_rm
= true
;
2653 protected function marketingJoins()
2655 if ($this->with_rm
) {
2656 return array('rm' => PlSqlJoin
::left('register_marketing', '$ME.uid = $UID'));
2664 // {{{ class ProfileFilter
2665 class ProfileFilter
extends UserFilter
2667 public function get($limit = null
)
2669 return $this->getProfiles($limit);
2672 public function filter(array $profiles, $limit = null
)
2674 return $this->filterProfiles($profiles, self
::defaultLimit($limit));
2677 public function getTotalCount()
2679 return $this->getTotalProfileCount();
2684 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: