2 /***************************************************************************
3 * Copyright (C) 2003-2014 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 ***************************************************************************/
22 require_once dirname(__FILE__
) . '/userfilter/conditions.inc.php';
23 require_once dirname(__FILE__
) . '/userfilter/orders.inc.php';
25 /***********************************
26 *********************************
28 *********************************
29 ***********************************/
31 // {{{ class UserFilter
32 /** This class provides a convenient and centralized way of filtering users.
35 * $uf = new UserFilter(new UFC_Blah($x, $y), new UFO_Coin($z, $t));
37 * Resulting UserFilter can be used to:
38 * - get a list of User objects matching the filter
39 * - get a list of UIDs matching the filter
40 * - get the number of users matching the filter
41 * - check whether a given User matches the filter
42 * - filter a list of User objects depending on whether they match the filter
44 * Usage for UFC and UFO objects:
45 * A UserFilter will call all private functions named XXXJoins.
46 * These functions must return an array containing the list of join
47 * required by the various UFC and UFO associated to the UserFilter.
48 * Entries in those returned array are of the following form:
49 * 'join_tablealias' => array('join_type', 'joined_table', 'join_criter')
50 * which will be translated into :
51 * join_type JOIN joined_table AS join_tablealias ON (join_criter)
54 * In the join_criter text, $ME is replaced with 'join_tablealias', $PID with
55 * profile.pid, and $UID with accounts.uid.
57 * For each kind of "JOIN" needed, a function named addXXXFilter() should be defined;
58 * its parameter will be used to set various private vars of the UserFilter describing
59 * the required joins ; such a function shall return the "join_tablealias" to use
60 * when referring to the joined table.
62 * For example, if data from profile_job must be available to filter results,
63 * the UFC object will call $uf-addJobFilter(), which will set the 'with_pj' var and
64 * return 'pj', the short name to use when referring to profile_job; when building
65 * the query, calling the jobJoins function will return an array containing a single
67 * 'pj' => array('left', 'profile_job', '$ME.pid = $UID');
69 * The 'register_optional' function can be used to generate unique table aliases when
70 * the same table has to be joined several times with different aliases.
72 class UserFilter
extends PlFilter
74 protected $joinMethods = array();
76 protected $joinMetas = array(
82 private $sort = array();
83 private $grouper = null
;
84 private $query = null
;
85 private $orderby = null
;
87 // Store the current 'search' visibility.
88 private $visibility = null
;
89 // If the 'search' visibility should be based on a DB field instead.
90 private $visibility_field = null
;
92 private $lastusercount = null
;
93 private $lastprofilecount = null
;
95 public function __construct($cond = null
, $sort = null
)
97 if (empty($this->joinMethods
)) {
98 $class = new ReflectionClass('UserFilter');
99 foreach ($class->getMethods() as $method) {
100 $name = $method->getName();
101 if (substr($name, -5) == 'Joins' && $name != 'buildJoins') {
102 $this->joinMethods
[] = $name;
106 if (!is_null($cond)) {
107 if ($cond instanceof PlFilterCondition
) {
108 $this->setCondition($cond);
111 if (!is_null($sort)) {
112 if ($sort instanceof PlFilterOrder
) {
113 $this->addSort($sort);
114 } else if (is_array($sort)) {
115 foreach ($sort as $s) {
121 // This will set the visibility to the default correct level.
122 $this->visibility
= Visibility
::defaultForRead();
125 /** Get the SQL condition to filter by visibility level for a field.
126 * This will return a SQL condition which evaluates to True if the given
127 * field display level is available from the current access level.
128 * @param $field Name of the field holding a display level
129 * @return string SQL condition, properly escaped, for that field.
131 public function getVisibilityConditionForField($field)
133 if ($this->visibility_field
!= null
) {
134 // Use enum 'bit' arithmetic.
135 // Display levels are ordered as 'hidden, private, ax, public'
136 // Thus ax > private.
137 // The $sub.display_level cell will contain the 'most private' display
138 // level available based on $field. If it is 'ax' and $field is
139 // 'private','ax' <= 'private' is false.
140 $sub = $this->addVisibilityFieldFilter($this->visibility_field
);
141 return $sub . '.best_display_level + 0 <= 0 + ' . $field;
143 $sub = $this->addVisibilityAbsoluteFilter($this->visibility
->level());
144 return $sub . '.best_display_level + 0 <= 0 + ' . $field;
148 /** Get the SQL condition to filter by a given visibility level.
149 * @param $level One of Visibility::EXPORT_*
150 * @return string A SQL condition, properly escaped, which evaluates to 'true' if the $level can be viewed with the current access level.
152 public function getVisibilityConditionAbsolute($level)
154 if ($this->visibility_field
!= null
) {
155 // The $sub.display_levels cell will contain allowed display levels
156 // for an access level of $this->visibility_field.
157 $sub = $this->addVisibilityFieldFilter($this->visibility_field
);
158 return XDB
::format('FIND_IN_SET({?}, ' . $sub . '.display_levels)', $level);
160 if ($this->visibility
->isVisible($level)) {
168 private function buildQuery()
170 // The root condition is built first because some orders need info
171 // available only once all UFC have set their conditions (UFO_Score)
172 if (is_null($this->query
)) {
173 $where = $this->root
->buildCondition($this);
174 $where = str_replace(array_keys($this->joinMetas
),
178 if (is_null($this->orderby
)) {
180 foreach ($this->sort
as $sort) {
181 $orders = array_merge($orders, $sort->buildSort($this));
183 if (count($orders) == 0) {
186 $this->orderby
= 'ORDER BY ' . implode(', ', $orders);
188 $this->orderby
= str_replace(array_keys($this->joinMetas
),
192 if (is_null($this->query
)) {
193 if ($this->with_accounts
) {
194 $from = 'accounts AS a';
196 $this->requireProfiles();
197 $from = 'profiles AS p';
199 $joins = $this->buildJoins();
200 $this->query
= 'FROM ' . $from . '
202 WHERE (' . $where . ')';
206 public function hasGroups()
208 return $this->grouper
!= null
;
211 public function getGroups()
213 return $this->getUIDGroups();
216 public function getUIDGroups()
218 $this->requireAccounts();
220 $token = $this->grouper
->getGroupToken($this);
222 $groups = XDB
::rawFetchAllRow('SELECT ' . $token . ', COUNT(a.uid)
229 public function getPIDGroups()
231 $this->requireProfiles();
233 $token = $this->grouper
->getGroupToken($this);
235 $groups = XDB
::rawFetchAllRow('SELECT ' . $token . ', COUNT(p.pid)
242 private function getUIDList($uids = null
, PlLimit
$limit)
244 $this->requireAccounts();
246 $lim = $limit->getSql();
249 $cond = XDB
::format(' AND a.uid IN {?}', $uids);
251 $fetched = XDB
::rawFetchColumn('SELECT SQL_CALC_FOUND_ROWS a.uid
252 ' . $this->query
. $cond . '
254 ' . $this->orderby
. '
256 $this->lastusercount
= (int)XDB
::fetchOneCell('SELECT FOUND_ROWS()');
260 private function getPIDList($pids = null
, PlLimit
$limit)
262 $this->requireProfiles();
264 $lim = $limit->getSql();
266 if (!is_null($pids)) {
267 $cond = XDB
::format(' AND p.pid IN {?}', $pids);
269 $fetched = XDB
::rawFetchColumn('SELECT SQL_CALC_FOUND_ROWS p.pid
270 ' . $this->query
. $cond . '
272 ' . $this->orderby
. '
274 $this->lastprofilecount
= (int)XDB
::fetchOneCell('SELECT FOUND_ROWS()');
278 private static function defaultLimit($limit) {
279 if ($limit == null
) {
280 return new PlLimit();
286 /** Check that the user match the given rule.
288 public function checkUser(PlUser
$user)
290 $this->requireAccounts();
292 $count = (int)XDB
::rawFetchOneCell('SELECT COUNT(*)
294 . XDB
::format(' AND a.uid = {?}', $user->id()));
298 /** Check that the profile match the given rule.
300 public function checkProfile(Profile
$profile)
302 $this->requireProfiles();
304 $count = (int)XDB
::rawFetchOneCell('SELECT COUNT(*)
306 . XDB
::format(' AND p.pid = {?}', $profile->id()));
310 /** Default filter is on users
312 public function filter(array $users, $limit = null
)
314 return $this->filterUsers($users, self
::defaultLimit($limit));
317 /** Filter a list of users to extract the users matching the rule.
319 public function filterUsers(array $users, $limit = null
)
321 $limit = self
::defaultLimit($limit);
322 $this->requireAccounts();
326 foreach ($users as $user) {
327 if ($user instanceof PlUser
) {
333 $table[$uid] = $user;
335 $fetched = $this->getUIDList($uids, $limit);
337 foreach ($fetched as $uid) {
338 $output[] = $table[$uid];
343 /** Filter a list of profiles to extract the users matching the rule.
345 public function filterProfiles(array $profiles, $limit = null
)
347 $limit = self
::defaultLimit($limit);
348 $this->requireProfiles();
352 foreach ($profiles as $profile) {
353 if ($profile instanceof Profile
) {
354 $pid = $profile->id();
359 $table[$pid] = $profile;
361 $fetched = $this->getPIDList($pids, $limit);
363 foreach ($fetched as $pid) {
364 $output[] = $table[$pid];
369 public function getUIDs($limit = null
)
371 $limit = self
::defaultLimit($limit);
372 return $this->getUIDList(null
, $limit);
375 public function getUID($pos = 0)
377 $uids =$this->getUIDList(null
, new PlLimit(1, $pos));
378 if (count($uids) == 0) {
385 public function getPIDs($limit = null
)
387 $limit = self
::defaultLimit($limit);
388 return $this->getPIDList(null
, $limit);
391 public function getPID($pos = 0)
393 $pids =$this->getPIDList(null
, new PlLimit(1, $pos));
394 if (count($pids) == 0) {
401 public function getUsers($limit = null
)
403 return User
::getBulkUsersWithUIDs($this->getUIDs($limit));
406 public function getUser($pos = 0)
408 $uid = $this->getUID($pos);
412 return User
::getWithUID($uid);
416 public function iterUsers($limit = null
)
418 return User
::iterOverUIDs($this->getUIDs($limit));
421 public function getProfiles($limit = null
, $fields = 0x0000, $visibility = null
)
423 return Profile
::getBulkProfilesWithPIDs($this->getPIDs($limit), $fields, $visibility);
426 public function getProfile($pos = 0, $fields = 0x0000, $visibility = null
)
428 $pid = $this->getPID($pos);
432 return Profile
::get($pid, $fields, $visibility);
436 public function iterProfiles($limit = null
, $fields = 0x0000, $visibility = null
)
438 return Profile
::iterOverPIDs($this->getPIDs($limit), true
, $fields, $visibility);
441 public function get($limit = null
)
443 return $this->getUsers($limit);
446 public function getIds($limit = null
)
448 return $this->getUIDs();
451 public function getTotalCount()
453 return $this->getTotalUserCount();
456 public function getTotalUserCount()
458 if (is_null($this->lastusercount
)) {
459 $this->requireAccounts();
461 return (int)XDB
::rawFetchOneCell('SELECT COUNT(DISTINCT a.uid)
464 return $this->lastusercount
;
468 public function getTotalProfileCount()
470 if (is_null($this->lastprofilecount
)) {
471 $this->requireProfiles();
473 return (int)XDB
::rawFetchOneCell('SELECT COUNT(DISTINCT p.pid)
476 return $this->lastprofilecount
;
480 public function setCondition(PlFilterCondition
$cond)
482 $this->root
=& $cond;
486 public function addSort(PlFilterOrder
$sort)
488 if (count($this->sort
) == 0 && $sort instanceof PlFilterGroupableOrder
)
490 $this->grouper
= $sort;
492 $this->sort
[] = $sort;
493 $this->orderby
= null
;
496 public function export()
498 $export = array('conditions' => $this->root
->export());
499 if (!empty($this->sort
)) {
500 $export['sorts'] = array();
501 foreach ($this->sort
as $sort) {
502 $export['sorts'][] = $sort->export();
508 public function exportConditions()
510 return $this->root
->export();
513 public static function fromExport(array $export)
515 $export = new PlDict($export);
516 if (!$export->has('conditions')) {
517 throw new Exception("Cannot build a user filter without conditions");
519 $cond = UserFilterCondition
::fromExport($export->v('conditions'));
521 if ($export->has('sorts')) {
523 foreach ($export->v('sorts') as $sort) {
524 $sorts[] = UserFilterOrder
::fromExport($sort);
527 return new UserFilter($cond, $sorts);
530 public static function fromJSon($json)
532 $export = json_decode($json, true
);
533 if (is_null($export)) {
534 throw new Exception("Invalid json: $json");
536 return self
::fromExport($json);
539 public static function fromExportedConditions(array $export)
541 $cond = UserFilterCondition
::fromExport($export);
542 return new UserFilter($cond);
545 public static function fromJSonConditions($json)
547 $export = json_decode($json, true
);
548 if (is_null($export)) {
549 throw new Exception("Invalid json: $json");
551 return self
::fromExportedConditions($json);
554 static public function getLegacy($promo_min, $promo_max)
556 if ($promo_min != 0) {
557 $min = new UFC_Promo('>=', self
::GRADE_ING
, intval($promo_min));
559 $min = new PFC_True();
561 if ($promo_max != 0) {
562 $max = new UFC_Promo('<=', self
::GRADE_ING
, intval($promo_max));
564 $max = new PFC_True();
566 return new UserFilter(new PFC_And($min, $max));
569 static public function sortByName()
571 return array(new UFO_Name());
574 static public function sortByPromo()
576 return array(new UFO_Promo(), new UFO_Name());
579 static private function getDBSuffix($string)
581 if (is_array($string)) {
582 if (count($string) == 1) {
583 return self
::getDBSuffix(array_pop($string));
585 return md5(implode('|', $string));
587 return preg_replace('/[^a-z0-9]/i', '', $string);
592 /** Stores a new (and unique) table alias in the &$table table
593 * @param &$table Array in which the table alias must be stored
594 * @param $val Value which will then be used to build the join
595 * @return Name of the newly created alias
598 private function register_optional(array &$table, $val)
601 $sub = $this->option++
;
604 $sub = self
::getDBSuffix($val);
608 $table[$sub] = $index;
612 /** PROFILE VS ACCOUNT
614 private $with_profiles = false
;
615 private $with_accounts = false
;
616 public function requireAccounts()
618 $this->with_accounts
= true
;
621 public function accountsRequired()
623 return $this->with_accounts
;
626 public function requireProfiles()
628 $this->with_profiles
= true
;
631 public function profilesRequired()
633 return $this->with_profiles
;
636 protected function accountJoins()
639 if ($this->with_profiles
&& $this->with_accounts
) {
640 $joins['ap'] = PlSqlJoin
::left('account_profiles', '$ME.uid = $UID AND FIND_IN_SET(\'owner\', ap.perms)');
641 $joins['p'] = PlSqlJoin
::left('profiles', '$PID = ap.pid');
649 public function requirePerms()
651 $this->requireAccounts();
656 protected function permJoins()
659 return array('at' => PlSqlJoin
::left('account_types', '$ME.type = a.type'));
667 const DISPLAY
= 'display';
669 public function addDisplayFilter()
671 $this->requireProfiles();
676 protected function displayJoins()
679 return array('pd' => PlSqlJoin
::left('profile_display', '$ME.pid = $PID'));
688 private $with_logger = false
;
689 public function addLoggerFilter()
691 $this->with_logger
= true
;
692 $this->requireAccounts();
695 protected function loggerJoins()
698 if ($this->with_logger
) {
699 $joins['ls'] = PlSqlJoin
::left('log_sessions', '$ME.uid = $UID');
706 private $name_tokens = array();
707 private $nb_tokens = 0;
709 public function addNameTokensFilter($token)
711 $this->requireProfiles();
712 $sub = 'sn' . (1 +
$this->nb_tokens
);
714 $this->name_tokens
[$sub] = $token;
718 protected function nameTokensJoins()
720 /* We don't return joins, since with_sn forces the SELECT to run on search_name first */
722 foreach ($this->name_tokens
as $sub => $token) {
723 $joins[$sub] = PlSqlJoin
::left('search_name', '$ME.pid = $PID');
728 public function getNameTokens()
730 return $this->name_tokens
;
736 private $with_nat = false
;
737 public function addNationalityFilter()
739 $this->with_nat
= true
;
743 protected function nationalityJoins()
746 if ($this->with_nat
) {
747 $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');
754 const GRADE_ING
= Profile
::DEGREE_X
;
755 const GRADE_PHD
= Profile
::DEGREE_D
;
756 const GRADE_MST
= Profile
::DEGREE_M
;
757 static public function isGrade($grade)
759 return ($grade !== 0) && ($grade == self
::GRADE_ING ||
$grade == self
::GRADE_PHD ||
$grade == self
::GRADE_MST
);
762 static public function assertGrade($grade)
764 if (!self
::isGrade($grade)) {
765 Platal
::page()->killError("Diplôme non valide: $grade");
769 static public function promoYear($grade)
771 // XXX: Definition of promotion for phds and masters might change in near future.
772 return ($grade == UserFilter
::GRADE_ING
) ?
'entry_year' : 'grad_year';
775 private $pepe = array();
776 private $with_pee = false
;
777 public function addEducationFilter($x = false
, $grade = null
)
779 $this->requireProfiles();
781 $index = $this->option
;
782 $sub = $this->option++
;
784 self
::assertGrade($grade);
787 $this->with_pee
= true
;
790 $this->pepe
[$index] = $sub;
794 protected function educationJoins()
797 if ($this->with_pee
) {
798 $joins['pee'] = PlSqlJoin
::inner('profile_education_enum', 'pee.abbreviation = \'X\'');
800 foreach ($this->pepe
as $grade => $sub) {
801 if ($this->isGrade($grade)) {
802 $joins['pe' . $sub] = PlSqlJoin
::left('profile_education', '$ME.eduid = pee.id AND $ME.pid = $PID');
803 $joins['pede' . $sub] = PlSqlJoin
::inner('profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid AND $ME.degree LIKE {?}', $grade);
805 $joins['pe' . $sub] = PlSqlJoin
::left('profile_education', '$ME.pid = $PID');
806 $joins['pee' . $sub] = PlSqlJoin
::inner('profile_education_enum', '$ME.id = pe' . $sub . '.eduid');
807 $joins['pede' . $sub] = PlSqlJoin
::inner('profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid');
816 private $gpm = array();
817 public function addGroupFilter($group = null
)
819 $this->requireAccounts();
820 if (!is_null($group)) {
821 if (is_int($group) ||
ctype_digit($group)) {
822 $index = $sub = $group;
825 $sub = self
::getDBSuffix($group);
828 $sub = 'group_' . $this->option++
;
832 $this->gpm
[$sub] = $index;
836 private $gpfm = array();
837 public function addGroupFormerMemberFilter()
839 $this->requireAccounts();
840 $sub = '_' . $this->option++
;
841 $this->gpfm
[] = $sub;
845 protected function groupJoins()
848 foreach ($this->gpm
as $sub => $key) {
850 $joins['gpa' . $sub] = PlSqlJoin
::inner('groups');
851 $joins['gpm' . $sub] = PlSqlJoin
::left('group_members', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id');
852 } else if (is_int($key) ||
ctype_digit($key)) {
853 $joins['gpm' . $sub] = PlSqlJoin
::left('group_members', '$ME.uid = $UID AND $ME.asso_id = ' . $key);
855 $joins['gpa' . $sub] = PlSqlJoin
::inner('groups', '$ME.diminutif = {?}', $key);
856 $joins['gpm' . $sub] = PlSqlJoin
::left('group_members', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id');
859 foreach ($this->gpfm
as $sub) {
860 $joins['gpfm' . $sub] = PlSqlJoin
::left('group_former_members', '$ME.uid = $UID');
867 private $nls = array();
868 public function addNewsLetterFilter($nlid)
870 $this->requireAccounts();
871 $sub = 'nl_' . $nlid;
872 $this->nls
[$nlid] = $sub;
876 protected function newsLetterJoins()
879 foreach ($this->nls
as $key => $sub) {
880 $joins[$sub] = PlSqlJoin
::left('newsletter_ins', '$ME.nlid = {?} AND $ME.uid = $UID', $key);
888 private $with_bi = false
;
889 private $with_bd = false
;
890 public function addBinetsFilter($with_enum = false
)
892 $this->requireProfiles();
893 $this->with_bi
= true
;
895 $this->with_bd
= true
;
902 protected function binetsJoins()
905 if ($this->with_bi
) {
906 $joins['bi'] = PlSqlJoin
::left('profile_binets', '$ME.pid = $PID');
908 if ($this->with_bd
) {
909 $joins['bd'] = PlSqlJoin
::left('profile_binet_enum', '$ME.id = bi.binet_id');
916 private $ra = array();
917 /** Allows filtering by redirection.
918 * @param $email If null, enable a left join on the email redirection table
919 * (email_redirect_account); otherwise, perform a left join on users having
920 * that email as a redirection.
921 * @return Suffix to use to access the adequate table.
923 public function addEmailRedirectFilter($email = null
)
925 $this->requireAccounts();
926 return $this->register_optional($this->ra
, $email);
929 const ALIAS_BEST
= 'bestalias';
930 const ALIAS_FORLIFE
= 'forlife';
931 const ALIAS_AUXILIARY
= 'alias_aux';
932 private $sa = array();
933 /** Allows filtering by source email.
934 * @param $email If null, enable a left join on the email source table
935 * (email_source_account); otherwise, perform a left join on users having
936 * that email as a source email.
937 * @return Suffix to use to access the adequate table.
939 public function addAliasFilter($email = null
)
941 $this->requireAccounts();
942 return $this->register_optional($this->sa
, $email);
945 private $with_rf = false
;
946 /** Allows filtering by active redirection.
947 * @return Suffix to use to access the adequate table.
949 public function addActiveEmailRedirectFilter($email = null
)
951 $this->requireAccounts();
952 $this->with_rf
= true
;
955 protected function emailJoins()
959 foreach ($this->ra
as $sub => $redirections) {
960 if (is_null($redirections)) {
961 $joins['ra' . $sub] = PlSqlJoin
::left('email_redirect_account', '$ME.uid = $UID AND $ME.type != \'imap\'');
963 if (!is_array($redirections)) {
964 $key = array($redirections);
966 $joins['ra' . $sub] = PlSqlJoin
::left('email_redirect_account', '$ME.uid = $UID AND $ME.type != \'imap\'
967 AND $ME.redirect IN {?}', $redirections);
970 foreach ($this->sa
as $sub => $emails) {
971 if (is_null($emails)) {
972 $joins['sa' . $sub] = PlSqlJoin
::left('email_source_account', '$ME.uid = $UID');
973 } else if ($sub == self
::ALIAS_BEST
) {
974 $joins['sa' . $sub] = PlSqlJoin
::left('email_source_account', '$ME.uid = $UID AND FIND_IN_SET(\'bestalias\', $ME.flags)');
975 } else if ($sub == self
::ALIAS_FORLIFE
) {
976 $joins['sa' . $sub] = PlSqlJoin
::left('email_source_account', '$ME.uid = $UID AND $ME.type = \'forlife\'');
977 } else if ($sub == self
::ALIAS_AUXILIARY
) {
978 $joins['sa' . $sub] = PlSqlJoin
::left('email_source_account', '$ME.uid = $UID AND $ME.type = \'alias_aux\'');
980 if (!is_array($emails)) {
981 $key = array($emails);
983 $joins['sa' . $sub] = PlSqlJoin
::left('email_source_account', '$ME.uid = $UID AND $ME.email IN {?}', $emails);
986 if ($this->with_rf
) {
987 $joins['rf'] = PlSqlJoin
::left('email_redirect_account', '$ME.uid = $UID AND $ME.type != \'imap\' AND $ME.flags = \'active\'');;
995 private $types = array();
996 public function addAddressFilter($type)
998 $this->requireProfiles();
999 $this->with_pa
= true
;
1001 $sub = '_' . $this->option++
;
1002 $this->types
[$type] = $sub;
1006 protected function addressJoins()
1009 foreach ($this->types
as $type => $sub) {
1010 $joins['pa' . $sub] = PlSqlJoin
::inner('profile_addresses', '$ME.pid = $PID');
1011 $joins['pac' . $sub] = PlSqlJoin
::inner('profile_addresses_components',
1012 '$ME.pid = pa' . $sub . '.pid AND $ME.jobid = pa' . $sub . '.jobid AND $ME.groupid = pa' . $sub . '.groupid AND $ME.type = pa' . $sub . '.type AND $ME.id = pa' . $sub . '.id');
1013 $joins['pace' . $sub] = PlSqlJoin
::inner('profile_addresses_components_enum',
1014 '$ME.id = pac' . $sub . '.component_id AND FIND_IN_SET({?}, $ME.types)', $type);
1024 private $pc = false
;
1025 private $pce = array();
1026 private $pcr = false
;
1027 public function addCorpsFilter($type)
1029 $this->requireProfiles();
1031 if ($type == UFC_Corps
::CURRENT
) {
1032 $this->pce
['pcec'] = 'current_corpsid';
1034 } else if ($type == UFC_Corps
::ORIGIN
) {
1035 $this->pce
['pceo'] = 'original_corpsid';
1040 public function addCorpsRankFilter()
1042 $this->requireProfiles();
1048 protected function corpsJoins()
1052 $joins['pc'] = PlSqlJoin
::left('profile_corps', '$ME.pid = $PID');
1055 $joins['pcr'] = PlSqlJoin
::left('profile_corps_rank_enum', '$ME.id = pc.rankid');
1057 foreach($this->pce
as $sub => $field) {
1058 $joins[$sub] = PlSqlJoin
::left('profile_corps_enum', '$ME.id = pc.' . $field);
1066 const JOB_USERDEFINED
= 0x0001;
1067 const JOB_CV
= 0x0002;
1068 const JOB_ANY
= 0x0003;
1072 * pje => profile_job_enum
1073 * pjt => profile_job_terms
1075 private $with_pj = false
;
1076 private $with_pje = false
;
1077 private $with_pjt = 0;
1079 public function addJobFilter()
1081 $this->requireProfiles();
1082 $this->with_pj
= true
;
1086 public function addJobCompanyFilter()
1088 $this->addJobFilter();
1089 $this->with_pje
= true
;
1094 * Adds a filter on job terms of profile.
1095 * @param $nb the number of job terms to use
1096 * @return an array of the fields to filter (one for each term).
1098 public function addJobTermsFilter($nb = 1)
1100 $this->with_pjt
= $nb;
1101 $jobtermstable = array();
1102 for ($i = 1; $i <= $nb; ++
$i) {
1103 $jobtermstable[] = 'pjtr_'.$i;
1105 return $jobtermstable;
1108 protected function jobJoins()
1111 if ($this->with_pj
) {
1112 $joins['pj'] = PlSqlJoin
::left('profile_job', '$ME.pid = $PID');
1114 if ($this->with_pje
) {
1115 $joins['pje'] = PlSqlJoin
::left('profile_job_enum', '$ME.id = pj.jobid');
1117 if ($this->with_pjt
> 0) {
1118 for ($i = 1; $i <= $this->with_pjt
; ++
$i) {
1119 $joins['pjt_'.$i] = PlSqlJoin
::left('profile_job_term', '$ME.pid = $PID');
1120 $joins['pjtr_'.$i] = PlSqlJoin
::left('profile_job_term_relation', '$ME.jtid_2 = pjt_'.$i.'.jtid');
1129 private $with_pnw = false
;
1130 public function addNetworkingFilter()
1132 $this->requireAccounts();
1133 $this->with_pnw
= true
;
1137 protected function networkingJoins()
1140 if ($this->with_pnw
) {
1141 $joins['pnw'] = PlSqlJoin
::left('profile_networking', '$ME.pid = $PID');
1149 private $with_ptel = false
;
1151 public function addPhoneFilter()
1153 $this->requireAccounts();
1154 $this->with_ptel
= true
;
1158 protected function phoneJoins()
1161 if ($this->with_ptel
) {
1162 $joins['ptel'] = PlSqlJoin
::left('profile_phones', '$ME.pid = $PID');
1170 private $with_pmed = false
;
1171 public function addMedalFilter()
1173 $this->requireProfiles();
1174 $this->with_pmed
= true
;
1178 protected function medalJoins()
1181 if ($this->with_pmed
) {
1182 $joins['pmed'] = PlSqlJoin
::left('profile_medals', '$ME.pid = $PID');
1189 private $dts = array();
1191 const DELTATEN_MESSAGE
= 2;
1194 public function addDeltaTenFilter($type)
1196 $this->requireProfiles();
1198 case self
::DELTATEN
:
1199 $this->dts
['pdt'] = 'profile_deltaten';
1201 case self
::DELTATEN_MESSAGE
:
1202 $this->dts
['pdtm'] = 'profile_deltaten';
1205 Platal
::page()->killError("Undefined DeltaTen filter.");
1209 protected function deltatenJoins()
1212 foreach ($this->dts
as $sub => $tab) {
1213 $joins[$sub] = PlSqlJoin
::left($tab, '$ME.pid = $PID');
1221 private $pms = array();
1222 private $mjtr = false
;
1224 const MENTOR_EXPERTISE
= 2;
1225 const MENTOR_COUNTRY
= 3;
1226 const MENTOR_TERM
= 4;
1228 public function addMentorFilter($type)
1230 $this->requireProfiles();
1233 $this->pms
['pm'] = 'profile_mentor';
1235 case self
::MENTOR_EXPERTISE
:
1236 $this->pms
['pme'] = 'profile_mentor';
1238 case self
::MENTOR_COUNTRY
:
1239 $this->pms
['pmc'] = 'profile_mentor_country';
1241 case self
::MENTOR_TERM
:
1242 $this->pms
['pmt'] = 'profile_mentor_term';
1246 Platal
::page()->killError("Undefined mentor filter.");
1250 protected function mentorJoins()
1253 foreach ($this->pms
as $sub => $tab) {
1254 $joins[$sub] = PlSqlJoin
::left($tab, '$ME.pid = $PID');
1257 $joins['mjtr'] = PlSqlJoin
::left('profile_job_term_relation', '$ME.jtid_2 = pmt.jtid');
1264 private $cts = array();
1265 public function addContactFilter($uid = null
)
1267 $this->requireProfiles();
1268 return $this->register_optional($this->cts
, is_null($uid) ? null
: 'user_' . $uid);
1271 protected function contactJoins()
1274 foreach ($this->cts
as $sub=>$key) {
1275 if (is_null($key)) {
1276 $joins['c' . $sub] = PlSqlJoin
::left('contacts', '$ME.contact = $PID');
1278 $joins['c' . $sub] = PlSqlJoin
::left('contacts', '$ME.uid = {?} AND $ME.contact = $PID', substr($key, 5));
1287 private $wn = array();
1288 public function addWatchRegistrationFilter($uid = null
)
1290 $this->requireAccounts();
1291 return $this->register_optional($this->wn
, is_null($uid) ? null
: 'user_' . $uid);
1294 private $wp = array();
1295 public function addWatchPromoFilter($uid = null
)
1297 $this->requireAccounts();
1298 return $this->register_optional($this->wp
, is_null($uid) ? null
: 'user_' . $uid);
1301 private $w = array();
1302 public function addWatchFilter($uid = null
)
1304 $this->requireAccounts();
1305 return $this->register_optional($this->w
, is_null($uid) ? null
: 'user_' . $uid);
1308 protected function watchJoins()
1311 foreach ($this->w
as $sub=>$key) {
1312 if (is_null($key)) {
1313 $joins['w' . $sub] = PlSqlJoin
::left('watch');
1315 $joins['w' . $sub] = PlSqlJoin
::left('watch', '$ME.uid = {?}', substr($key, 5));
1318 foreach ($this->wn
as $sub=>$key) {
1319 if (is_null($key)) {
1320 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.ni_id = $UID');
1322 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5));
1325 foreach ($this->wn
as $sub=>$key) {
1326 if (is_null($key)) {
1327 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.ni_id = $UID');
1329 $joins['wn' . $sub] = PlSqlJoin
::left('watch_nonins', '$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5));
1332 foreach ($this->wp
as $sub=>$key) {
1333 if (is_null($key)) {
1334 $joins['wp' . $sub] = PlSqlJoin
::left('watch_promo');
1336 $joins['wp' . $sub] = PlSqlJoin
::left('watch_promo', '$ME.uid = {?}', substr($key, 5));
1345 private $with_photo;
1346 public function addPhotoFilter()
1348 $this->requireProfiles();
1349 $this->with_photo
= true
;
1353 protected function photoJoins()
1355 if ($this->with_photo
) {
1356 return array('photo' => PlSqlJoin
::left('profile_photos', '$ME.pid = $PID'));
1366 public function addMarketingHash()
1368 $this->requireAccounts();
1369 $this->with_rm
= true
;
1372 protected function marketingJoins()
1374 if ($this->with_rm
) {
1375 return array('rm' => PlSqlJoin
::left('register_marketing', '$ME.uid = $UID'));
1385 // Lists partner shortnames in use, as a $partner_shortname => true map.
1386 private $ppss = array();
1388 /** Add a filter on user having settings for a given partner.
1389 * @param $partner_id the ID of the partner
1390 * @return the name of the table to use in joins (e.g ppss_$partner_id).
1392 public function addPartnerSharingFilter($partner_id)
1394 $this->requireProfiles();
1395 $sub = "ppss_" . $partner_id;
1396 $this->ppss
[$sub] = $partner_id;
1400 protected function partnerSharingJoins()
1403 foreach ($this->ppss
as $sub => $partner_id) {
1404 $joins[$sub] = PlSqlJoin
::left('profile_partnersharing_settings', '$ME.pid = $PID AND $ME.partner_id = {?} AND $ME.sharing_level != \'none\'', $partner_id);
1409 public function restrictVisibilityForPartner($partner_id)
1411 $sub = $this->addPartnerSharingFilter($partner_id);
1412 $this->visibility_field
= $sub . '.sharing_level';
1417 private $vlevels = array();
1418 private $vfields = array();
1419 public function addVisibilityAbsoluteFilter($level)
1421 $sub = 'pvel_' . $level;
1422 $this->vlevels
[$level] = $sub;
1426 public function addVisibilityFieldFilter($field)
1428 $sub = 'pvef_' . self
::getDBSuffix($field);
1429 $this->vfields
[$field] = $sub;
1433 /** Since this method might perform inner joins on tables which have been
1434 * joined previously (e.g when using addVisibilityFieldFilter), it has to
1435 * come after the Joins() methods for those tables.
1436 * This is due to the implementation logic for discovering joins and the
1437 * ordering used by PHP introspection.
1439 protected function visibilityJoins()
1442 foreach ($this->vlevels
as $level => $sub) {
1443 $joins[$sub] = PlSqlJoin
::inner('profile_visibility_enum', '$ME.access_level = {?}', $level);
1445 foreach ($this->vfields
as $field => $sub) {
1446 $joins[$sub] = PlSqlJoin
::inner('profile_visibility_enum', '$ME.access_level = ' . $field);
1453 // {{{ class ProfileFilter
1454 class ProfileFilter
extends UserFilter
1456 public function get($limit = null
)
1458 return $this->getProfiles($limit);
1461 public function getIds($limit = null
)
1463 return $this->getPIDs();
1466 public function filter(array $profiles, $limit = null
)
1468 return $this->filterProfiles($profiles, self
::defaultLimit($limit));
1471 public function getTotalCount()
1473 return $this->getTotalProfileCount();
1476 public function getGroups()
1478 return $this->getPIDGroups();
1483 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8: