X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=classes%2Fuserfilter.php;h=14e95578dc5bd85fbddf6761f8a0bb3f3b9d1ba5;hb=4ad7b8ca262ce5badfff6d5f23deb091913f0606;hp=2d33bca3f2bc702a9f98b5246f36213d9f01f5f8;hpb=34465ab035104f739a54ec62660f04f874448627;p=platal.git diff --git a/classes/userfilter.php b/classes/userfilter.php index 2d33bca..14e9557 100644 --- a/classes/userfilter.php +++ b/classes/userfilter.php @@ -46,11 +46,62 @@ class UFC_HasProfile implements UserFilterCondition { public function buildCondition(PlFilter &$uf) { + $uf->requireProfiles(); return '$PID IS NOT NULL'; } } // }}} +// {{{ class UFC_AccountType +/** Filters users who have one of the given account types + */ +class UFC_AccountType implements UserFilterCondition +{ + private $types; + + public function __construct() + { + $this->types = pl_flatten(func_get_args()); + } + + public function buildCondition(PlFilter &$uf) + { + $uf->requireAccounts(); + return XDB::format('a.type IN {?}', $this->types); + } +} +// }}} + +// {{{ class UFC_AccountPerm +/** Filters users who have one of the given permissions + */ +class UFC_AccountPerm implements UserFilterCondition +{ + private $perms; + + public function __construct() + { + $this->perms = pl_flatten(func_get_args()); + } + + public function buildCondition(PlFilter &$uf) + { + $uf->requirePerms(); + $conds = array(); + foreach ($this->perms as $perm) { + $conds[] = XDB::format('FIND_IN_SET({?}, IF(a.user_perms IS NULL, at.perms, + CONCAT(at.perms, \',\', a.user_perms)))', + $perm); + } + if (empty($conds)) { + return self::COND_TRUE; + } else { + return implode(' OR ', $conds); + } + } +} +// }}} + // {{{ class UFC_Hruid /** Filters users based on their hruid * @param $val Either an hruid, or a list of those @@ -59,17 +110,14 @@ class UFC_Hruid implements UserFilterCondition { private $hruids; - public function __construct($val) + public function __construct() { - if (!is_array($val)) { - $val = array($val); - } - $this->hruids = $val; + $this->hruids = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) { - $ufc->requireAccounts(); + $uf->requireAccounts(); return XDB::format('a.hruid IN {?}', $this->hruids); } } @@ -83,12 +131,9 @@ class UFC_Hrpid implements UserFilterCondition { private $hrpids; - public function __construct($val) + public function __construct() { - if (!is_array($val)) { - $val = array($val); - } - $this->hrpids = $val; + $this->hrpids = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) @@ -134,7 +179,7 @@ class UFC_Comment implements UserFilterCondition public function buildCondition(PlFilter &$uf) { $uf->requireProfiles(); - return 'p.freetext ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->text); + return $uf->getVisibilityCondition('p.freetext_pub') . ' AND p.freetext ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->text); } } // }}} @@ -160,6 +205,10 @@ class UFC_Promo implements UserFilterCondition if ($this->grade != UserFilter::DISPLAY) { UserFilter::assertGrade($this->grade); } + if ($this->grade == UserFilter::DISPLAY && $this->comparison != '=') { + // XXX: we might try to guess the grade from the first char of the promo and forbid only '<= 2004', but allow '<= X2004' + Platal::page()->killError("Il n'est pas possible d'appliquer la comparaison '" . $this->comparison . "' aux promotions sans spécifier de formation (X/M/D)"); + } } public function buildCondition(PlFilter &$uf) @@ -181,7 +230,7 @@ class UFC_Promo implements UserFilterCondition * @param type Parameter type (Xorg, AX, School) * @param value School id value */ -class UFC_SchooldId implements UserFilterCondition +class UFC_SchoolId implements UserFilterCondition { const AX = 'ax'; const Xorg = 'xorg'; @@ -226,12 +275,9 @@ class UFC_EducationSchool implements UserFilterCondition { private $val; - public function __construct($val) + public function __construct() { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; + $this->val = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) @@ -247,18 +293,15 @@ class UFC_EducationDegree implements UserFilterCondition { private $diploma; - public function __construct($diploma) + public function __construct() { - if (! is_array($diploma)) { - $diploma = array($diploma); - } - $this->diploma = $diploma; + $this->diploma = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) { $sub = $uf->addEducationFilter(); - return XDB::format('pee' . $sub . '.degreeid IN {?}', $this->val); + return XDB::format('pe' . $sub . '.degreeid IN {?}', $this->diploma); } } // }}} @@ -268,18 +311,15 @@ class UFC_EducationField implements UserFilterCondition { private $val; - public function __construct($val) + public function __construct() { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; + $this->val = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) { $sub = $uf->addEducationFilter(); - return XDB::format('pee' . $sub . '.fieldid IN {?}', $this->val); + return XDB::format('pe' . $sub . '.fieldid IN {?}', $this->val); } } // }}} @@ -292,10 +332,11 @@ class UFC_EducationField implements UserFilterCondition */ class UFC_Name implements UserFilterCondition { - const PREFIX = XDB::WILDCARD_PREFIX; // 0x001 - const SUFFIX = XDB::WILDCARD_SUFFIX; // 0x002 + const EXACT = XDB::WILDCARD_EXACT; // 0x000 + const PREFIX = XDB::WILDCARD_PREFIX; // 0x001 + const SUFFIX = XDB::WILDCARD_SUFFIX; // 0x002 const CONTAINS = XDB::WILDCARD_CONTAINS; // 0x003 - const PARTICLE = 0x007; // self::CONTAINS | 0x004 + const PARTICLE = 0x004; const VARIANTS = 0x008; private $type; @@ -353,7 +394,11 @@ class UFC_NameTokens implements UserFilterCondition public function __construct($tokens, $flags = array(), $soundex = false, $exact = false) { - $this->tokens = $tokens; + if (is_array($tokens)) { + $this->tokens = $tokens; + } else { + $this->tokens = array($tokens); + } if (is_array($flags)) { $this->flags = $flags; } else { @@ -365,22 +410,20 @@ class UFC_NameTokens implements UserFilterCondition public function buildCondition(PlFilter &$uf) { - $sub = $uf->addNameTokensFilter(!($this->exact || $this->soundex)); $conds = array(); - if ($this->soundex) { - $conds[] = XDB::format($sub . '.soundex IN {?}', $this->tokens); - } else if ($this->exact) { - $conds[] = XDB::format($sub . '.token IN {?}', $this->tokens); - } else { - $tokconds = array(); - foreach ($this->tokens as $token) { - $tokconds[] = $sub . '.token ' . XDB::formatWildcards(XDB::WILDCARD_PREFIX, $token); + foreach ($this->tokens as $i => $token) { + $sub = $uf->addNameTokensFilter($token); + if ($this->soundex) { + $c = XDB::format($sub . '.soundex = {?}', soundex_fr($token)); + } else if ($this->exact) { + $c = XDB::format($sub . '.token = {?}', $token); + } else { + $c = $sub . '.token ' . XDB::formatWildcards(XDB::WILDCARD_PREFIX, $token); } - $conds[] = implode(' OR ', $tokconds); - } - - if ($this->flags != null) { - $conds[] = XDB::format($sub . '.flags IN {?}', $this->flags); + if ($this->flags != null) { + $c .= XDB::format(' AND ' . $sub . '.flags IN {?}', $this->flags); + } + $conds[] = $c; } return implode(' AND ', $conds); @@ -393,12 +436,9 @@ class UFC_Nationality implements UserFilterCondition { private $val; - public function __construct($val) + public function __construct() { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; + $this->val = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) @@ -418,7 +458,7 @@ class UFC_Nationality implements UserFilterCondition // {{{ class UFC_Dead /** Filters users based on death date * @param $comparison Comparison operator - * @param $date Date to which death date should be compared + * @param $date Date to which death date should be compared (DateTime object, string or timestamp) */ class UFC_Dead implements UserFilterCondition { @@ -428,7 +468,7 @@ class UFC_Dead implements UserFilterCondition public function __construct($comparison = null, $date = null) { $this->comparison = $comparison; - $this->date = $date; + $this->date = make_datetime($date); } public function buildCondition(PlFilter &$uf) @@ -436,7 +476,7 @@ class UFC_Dead implements UserFilterCondition $uf->requireProfiles(); $str = 'p.deathdate IS NOT NULL'; if (!is_null($this->comparison)) { - $str .= ' AND p.deathdate ' . $this->comparison . ' ' . XDB::format('{?}', date('Y-m-d', $this->date)); + $str .= ' AND p.deathdate ' . $this->comparison . ' ' . XDB::format('{?}', $this->date->format('Y-m-d')); } return $str; } @@ -459,19 +499,19 @@ class UFC_Registered implements UserFilterCondition { $this->active = $active; $this->comparison = $comparison; - $this->date = $date; + $this->date = make_datetime($date); } public function buildCondition(PlFilter &$uf) { $uf->requireAccounts(); if ($this->active) { - $date = 'a.uid IS NOT NULL AND a.state = \'active\''; + $date = '$UID IS NOT NULL AND a.state = \'active\''; } else { - $date = 'a.uid IS NOT NULL AND a.state != \'pending\''; + $date = '$UID IS NOT NULL AND a.state != \'pending\''; } if (!is_null($this->comparison)) { - $date .= ' AND a.registration_date ' . $this->comparison . ' ' . XDB::format('{?}', date('Y-m-d', $this->date)); + $date .= ' AND a.registration_date != \'0000-00-00 00:00:00\' AND a.registration_date ' . $this->comparison . ' ' . XDB::format('{?}', $this->date->format('Y-m-d')); } return $date; } @@ -567,6 +607,10 @@ class UFC_Group implements UserFilterCondition public function buildCondition(PlFilter &$uf) { + // Groups have AX visibility. + if ($uf->getVisibilityLevel() == ProfileVisibility::VIS_PUBLIC) { + return self::COND_TRUE; + } $sub = $uf->addGroupFilter($this->group); $where = 'gpm' . $sub . '.perms IS NOT NULL'; if ($this->anim) { @@ -585,16 +629,17 @@ class UFC_Binet implements UserFilterCondition { private $val; - public function __construct($val) + public function __construct() { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; + $this->val = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) { + // Binets are private. + if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) { + return self::CONF_TRUE; + } $sub = $uf->addBinetsFilter(); return XDB::format($sub . '.binet_id IN {?}', $this->val); } @@ -609,15 +654,19 @@ class UFC_Section implements UserFilterCondition { private $section; - public function __construct($section) + public function __construct() { - $this->section = $section; + $this->section = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) { + // Sections are private. + if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) { + return self::CONF_TRUE; + } $uf->requireProfiles(); - return 'p.section = ' . XDB::format('{?}', $this->section); + return XDB::format('p.section IN {?}', $this->section); } } // }}} @@ -631,7 +680,7 @@ class UFC_Email implements UserFilterCondition private $emails; public function __construct() { - $this->emails = func_get_args(); + $this->emails = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) @@ -720,9 +769,10 @@ abstract class UFC_Address implements UserFilterCondition $this->flags = $flags; } - protected function initConds($sub) + protected function initConds($sub, $vis_cond) { - $conds = array(); + $conds = array($vis_cond); + $types = array(); foreach (self::$typetexts as $flag => $type) { if ($flag & $this->type) { @@ -730,7 +780,7 @@ abstract class UFC_Address implements UserFilterCondition } } if (count($types)) { - $conds[] = XDB::foramt($sub . '.type IN {?}', $types); + $conds[] = XDB::format($sub . '.type IN {?}', $types); } if ($this->flags != self::FLAG_ANY) { @@ -779,7 +829,7 @@ class UFC_AddressText extends UFC_Address public function buildCondition(PlFilter &$uf) { $sub = $uf->addAddressFilter(); - $conds = $this->initConds($sub); + $conds = $this->initConds($sub, $uf->getVisibilityCondition($sub . '.pub')); if ($this->text != null) { $conds[] = $sub . '.text' . $this->mkMatch($this->text); } @@ -836,7 +886,7 @@ class UFC_AddressField extends UFC_Address public function buildCondition(PlFilter &$uf) { $sub = $uf->addAddressFilter(); - $conds = $this->initConds($sub); + $conds = $this->initConds($sub, $uf->getVisibilityCondition($sub . '.pub')); switch ($this->fieldtype) { case self::FIELD_COUNTRY: @@ -892,6 +942,7 @@ class UFC_Corps implements UserFilterCondition */ $sub = $uf->addCorpsFilter($this->type); $cond = $sub . '.abbreviation = ' . $corps; + $cond .= ' AND ' . $uf->getVisibilityCondition($sub . '.corps_pub'); return $cond; } } @@ -912,10 +963,14 @@ class UFC_Corps_Rank implements UserFilterCondition public function buildCondition(PlFilter &$uf) { /** Tables shortcuts: + * pc for profile_corps * pcr for profile_corps_rank */ $sub = $uf->addCorpsRankFilter(); $cond = $sub . '.abbreviation = ' . $rank; + // XXX(x2006barrois): find a way to get rid of that hardcoded + // reference to 'pc'. + $cond .= ' AND ' . $uf->getVisibilityCondition('pc.corps_pub'); return $cond; } } @@ -952,43 +1007,41 @@ class UFC_Job_Company implements UserFilterCondition public function buildCondition(PlFilter &$uf) { $sub = $uf->addJobCompanyFilter(); - $cond = $sub . '.' . $this->type . ' = ' . XDB::format('{?}', $this->value); + $cond = $sub . '.' . $this->type . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->value); + $jsub = $uf->addJobFilter(); + $cond .= ' AND ' . $uf->getVisibilityCondition($jsub . '.pub'); return $cond; } } // }}} -// {{{ class UFC_Job_Sectorization -/** Filters users based on the ((sub)sub)sector they work in - * @param $val The ID of the sector, or an array of such IDs - * @param $type The kind of search (subsubsector/subsector/sector) +// {{{ class UFC_Job_Terms +/** Filters users based on the job terms they assigned to one of their + * jobs. + * @param $val The ID of the job term, or an array of such IDs */ -class UFC_Job_Sectorization implements UserFilterCondition +class UFC_Job_Terms implements UserFilterCondition { private $val; - private $type; - public function __construct($val, $type = UserFilter::JOB_SECTOR) + public function __construct($val) { - self::assertType($type); if (!is_array($val)) { $val = array($val); } $this->val = $val; - $this->type = $type; - } - - private static function assertType($type) - { - if ($type != UserFilter::JOB_SECTOR && $type != UserFilter::JOB_SUBSECTOR && $type != UserFilter::JOB_SUBSUBSECTOR) { - Platal::page()->killError("Type de secteur non valide."); - } } public function buildCondition(PlFilter &$uf) { - $sub = $uf->addJobSectorizationFilter($this->type); - return $sub . '.id = ' . XDB::format('{?}', $this->val); + $sub = $uf->addJobTermsFilter(count($this->val)); + $conditions = array(); + foreach ($this->val as $i => $jtid) { + $conditions[] = $sub[$i] . '.jtid_1 = ' . XDB::escape($jtid); + } + $jsub = $uf->addJobFilter(); + $conditions[] = $uf->getVisibilityCondition($jsub . '.pub'); + return implode(' AND ', $conditions); } } // }}} @@ -996,7 +1049,7 @@ class UFC_Job_Sectorization implements UserFilterCondition // {{{ class UFC_Job_Description /** Filters users based on their job description * @param $description The text being searched for - * @param $fields The fields to search for (user-defined, ((sub|)sub|)sector) + * @param $fields The fields to search for (CV, user-defined) */ class UFC_Job_Description implements UserFilterCondition { @@ -1013,28 +1066,25 @@ class UFC_Job_Description implements UserFilterCondition public function buildCondition(PlFilter &$uf) { $conds = array(); + + $jsub = $uf->addJobFilter(); + // CV is private => if only CV requested, and not private, + // don't do anything. Otherwise restrict to standard job visibility. + if ($this->fields == UserFilter::JOB_CV) { + if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) { + return self::CONF_TRUE; + } + } else { + $conds[] = $uf->getVisibilityCondition($jsub . '.pub'); + } + if ($this->fields & UserFilter::JOB_USERDEFINED) { - $sub = $uf->addJobFilter(); - $conds[] = $sub . '.description ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); + $conds[] = $jsub . '.description ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); } - if ($this->fields & UserFilter::JOB_CV) { + if ($this->fields & UserFilter::JOB_CV && $uf->getVisibilityLevel() == ProfileVisibility::VIS_PRIVATE) { $uf->requireProfiles(); $conds[] = 'p.cv ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); } - if ($this->fields & UserFilter::JOB_SECTOR) { - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SECTOR); - $conds[] = $sub . '.name ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); - } - if ($this->fields & UserFilter::JOB_SUBSECTOR) { - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SUBSECTOR); - $conds[] = $sub . '.name ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); - } - if ($this->fields & UserFilter::JOB_SUBSUBSECTOR) { - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SUBSUBSECTOR); - $conds[] = $sub . '.name ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_ALTERNATES); - $conds[] = $sub . '.name ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description); - } return implode(' OR ', $conds); } } @@ -1060,9 +1110,10 @@ class UFC_Networking implements UserFilterCondition { $sub = $uf->addNetworkingFilter(); $conds = array(); + $conds[] = $uf->getVisibilityCondition($sub . '.pub'); $conds[] = $sub . '.address ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->value); if ($this->type != -1) { - $conds[] = $sub . '.network_type = ' . XDB::format('{?}', $this->type); + $conds[] = $sub . '.nwid = ' . XDB::format('{?}', $this->type); } return implode(' AND ', $conds); } @@ -1093,16 +1144,20 @@ class UFC_Phone implements UserFilterCondition public function __construct($number, $num_type = self::NUM_ANY, $phone_type = self::PHONE_ANY) { - require_once('profil.func.inc.php'); - $this->number = $number; + $phone = new Phone(array('display' => $number)); + $phone->format(); + $this->number = $phone->search(); $this->num_type = $num_type; - $this->phone_type = format_phone_number($phone_type); + $this->phone_type = $phone_type; } public function buildCondition(PlFilter &$uf) { $sub = $uf->addPhoneFilter(); $conds = array(); + + $conds[] = $uf->getVisibilityCondition($sub . '.pub'); + $conds[] = $sub . '.search_tel = ' . XDB::format('{?}', $this->number); if ($this->num_type != self::NUM_ANY) { $conds[] = $sub . '.link_type = ' . XDB::format('{?}', $this->num_type); @@ -1134,7 +1189,12 @@ class UFC_Medal implements UserFilterCondition public function buildCondition(PlFilter &$uf) { $conds = array(); + + // This will require profiles => table 'p' will be available. $sub = $uf->addMedalFilter(); + + $conds[] = $uf->getVisibilityCondition('p.medals_pub'); + $conds[] = $sub . '.mid = ' . XDB::format('{?}', $this->medal); if ($this->grade != null) { $conds[] = $sub . '.gid = ' . XDB::format('{?}', $this->grade); @@ -1144,6 +1204,31 @@ class UFC_Medal implements UserFilterCondition } // }}} +// {{{ class UFC_Photo +/** Filters profiles with photo + */ +class UFC_Photo implements UserFilterCondition +{ + public function buildCondition(PlFilter &$uf) + { + $sub = $uf->addPhotoFilter(); + return $sub . '.attach IS NOT NULL AND ' . $uf->getVisibilityCondition($sub . '.pub'); + } +} +// }}} + +// {{{ class UFC_Mentor +class UFC_Mentor implements UserFilterCondition +{ + public function buildCondition(PlFilter &$uf) + { + $sub = $uf->addMentorFilter(UserFilter::MENTOR); + return $sub . '.expertise IS NOT NULL'; + } +} +// }}} + + // {{{ class UFC_Mentor_Expertise /** Filters users by mentoring expertise * @param $expertise Domain of expertise @@ -1173,46 +1258,36 @@ class UFC_Mentor_Country implements UserFilterCondition { private $country; - public function __construct($country) + public function __construct() { - $this->country = $country; + $this->country = pl_flatten(func_get_args()); } public function buildCondition(PlFilter &$uf) { $sub = $uf->addMentorFilter(UserFilter::MENTOR_COUNTRY); - return $sub . '.country = ' . XDB::format('{?}', $this->country); + return $sub . '.country IN ' . XDB::format('{?}', $this->country); } } // }}} -// {{{ class UFC_Mentor_Sectorization -/** Filters users based on mentoring (sub|)sector - * @param $sector ID of (sub)sector - * @param $type Whether we are looking for a sector or a subsector +// {{{ class UFC_Mentor_Terms +/** Filters users based on the job terms they used in mentoring. + * @param $val The ID of the job term, or an array of such IDs */ -class UFC_Mentor_Sectorization implements UserFilterCondition +class UFC_Mentor_Terms implements UserFilterCondition { - const SECTOR = 1; - const SUBSECTOR = 2; - private $sector; - private $type; + private $val; - public function __construct($sector, $type = self::SECTOR) + public function __construct($val) { - $this->sector = $sector; - $this->type = $type; + $this->val = $val; } public function buildCondition(PlFilter &$uf) { - $sub = $uf->addMentorFilter(UserFilter::MENTOR_SECTOR); - if ($this->type == self::SECTOR) { - $field = 'sectorid'; - } else { - $field = 'subsectorid'; - } - return $sub . '.' . $field . ' = ' . XDB::format('{?}', $this->sector); + $sub = $uf->addMentorFilter(UserFilter::MENTOR_TERM); + return $sub . '.jtid_1 = ' . XDB::escape($this->val); } } // }}} @@ -1332,28 +1407,12 @@ class UFC_MarketingHash implements UserFilterCondition * ORDERS ******************/ -// {{{ class UserFilterOrder -/** Base class for ordering results of a query. - * Parameters for the ordering must be given to the constructor ($desc for a - * descending order). - * The getSortTokens function is used to get actual ordering part of the query. - */ -abstract class UserFilterOrder extends PlFilterOrder -{ - /** This function must return the tokens to use for ordering - * @param &$uf The UserFilter whose results must be ordered - * @return The name of the field to use for ordering results - */ -// abstract protected function getSortTokens(UserFilter &$uf); -} -// }}} - // {{{ class UFO_Promo /** Orders users by promotion * @param $grade Formation whose promotion users should be sorted by (restricts results to users of that formation) * @param $desc Whether sort is descending */ -class UFO_Promo extends UserFilterOrder +class UFO_Promo extends PlFilterGroupableOrder { private $grade; @@ -1383,7 +1442,7 @@ class UFO_Promo extends UserFilterOrder * @param $particle Set to true if particles should be included in the sorting order * @param $desc If sort order should be descending */ -class UFO_Name extends UserFilterOrder +class UFO_Name extends PlFilterOrder { private $type; private $variant; @@ -1401,7 +1460,13 @@ class UFO_Name extends UserFilterOrder { if (Profile::isDisplayName($this->type)) { $sub = $uf->addDisplayFilter(); - return 'pd' . $sub . '.' . $this->type; + $token = 'pd' . $sub . '.' . $this->type; + if ($uf->accountsRequired()) { + $account_token = Profile::getAccountEquivalentName($this->type); + return 'IFNULL(' . $token . ', a.' . $account_token . ')'; + } else { + return $token; + } } else { $sub = $uf->addNameFilter($this->type, $this->variant); if ($this->particle) { @@ -1415,12 +1480,22 @@ class UFO_Name extends UserFilterOrder // }}} // {{{ class UFO_Score -class UFO_Score extends UserFilterOrder +class UFO_Score extends PlFilterOrder { protected function getSortTokens(PlFilter &$uf) { - $sub = $uf->addNameTokensFilter(); - return 'SUM(' . $sub . '.score)'; + $toks = $uf->getNameTokens(); + $scores = array(); + + // If there weren't any sort tokens, we shouldn't sort by score, sort by NULL instead + if (count($toks) == 0) { + return 'NULL'; + } + + foreach ($toks as $sub => $token) { + $scores[] = XDB::format('SUM(' . $sub . '.score + IF (' . $sub . '.token = {?}, 5, 0) )', $token); + } + return implode(' + ', $scores); } } // }}} @@ -1428,10 +1503,11 @@ class UFO_Score extends UserFilterOrder // {{{ class UFO_Registration /** Sorts users based on registration date */ -class UFO_Registration extends UserFilterOrder +class UFO_Registration extends PlFilterOrder { protected function getSortTokens(PlFilter &$uf) { + $uf->requireAccounts(); return 'a.registration_date'; } } @@ -1440,10 +1516,11 @@ class UFO_Registration extends UserFilterOrder // {{{ class UFO_Birthday /** Sorts users based on next birthday date */ -class UFO_Birthday extends UserFilterOrder +class UFO_Birthday extends PlFilterOrder { protected function getSortTokens(PlFilter &$uf) { + $uf->requireProfiles(); return 'p.next_birthday'; } } @@ -1452,10 +1529,11 @@ class UFO_Birthday extends UserFilterOrder // {{{ class UFO_ProfileUpdate /** Sorts users based on last profile update */ -class UFO_ProfileUpdate extends UserFilterOrder +class UFO_ProfileUpdate extends PlFilterOrder { protected function getSortTokens(PlFilter &$uf) { + $uf->requireProfiles(); return 'p.last_change'; } } @@ -1464,15 +1542,68 @@ class UFO_ProfileUpdate extends UserFilterOrder // {{{ class UFO_Death /** Sorts users based on death date */ -class UFO_Death extends UserFilterOrder +class UFO_Death extends PlFilterOrder { protected function getSortTokens(PlFilter &$uf) { + $uf->requireProfiles(); return 'p.deathdate'; } } // }}} +// {{{ class UFO_Uid +/** Sorts users based on their uid + */ +class UFO_Uid extends PlFilterOrder +{ + protected function getSortTokens(PlFilter &$uf) + { + $uf->requireAccounts(); + return '$UID'; + } +} +// }}} + +// {{{ class UFO_Hruid +/** Sorts users based on their hruid + */ +class UFO_Hruid extends PlFilterOrder +{ + protected function getSortTokens(PlFilter &$uf) + { + $uf->requireAccounts(); + return 'a.hruid'; + } +} +// }}} + +// {{{ class UFO_Pid +/** Sorts users based on their pid + */ +class UFO_Pid extends PlFilterOrder +{ + protected function getSortTokens(PlFilter &$uf) + { + $uf->requireProfiles(); + return '$PID'; + } +} +// }}} + +// {{{ class UFO_Hrpid +/** Sorts users based on their hrpid + */ +class UFO_Hrpid extends PlFilterOrder +{ + protected function getSortTokens(PlFilter &$uf) + { + $uf->requireProfiles(); + return 'p.hrpid'; + } +} +// }}} + /*********************************** ********************************* @@ -1512,7 +1643,7 @@ class UFO_Death extends UserFilterOrder * when referring to the joined table. * * For example, if data from profile_job must be available to filter results, - * the UFC object will call $uf-addJobFilter(), which will set the 'with_pj' var and + * the UFC object will call $uf-addJobFilter(), which will set the 'with_pj' var and * return 'pj', the short name to use when referring to profile_job; when building * the query, calling the jobJoins function will return an array containing a single * row: @@ -1532,10 +1663,15 @@ class UserFilter extends PlFilter private $root; private $sort = array(); + private $grouper = null; private $query = null; private $orderby = null; - private $lastcount = null; + // Store the current 'search' visibility. + private $profile_visibility = null; + + private $lastusercount = null; + private $lastprofilecount = null; public function __construct($cond = null, $sort = null) { @@ -1554,7 +1690,7 @@ class UserFilter extends PlFilter } } if (!is_null($sort)) { - if ($sort instanceof UserFilterOrder) { + if ($sort instanceof PlFilterOrder) { $this->addSort($sort); } else if (is_array($sort)) { foreach ($sort as $s) { @@ -1562,10 +1698,41 @@ class UserFilter extends PlFilter } } } + + // This will set the visibility to the default correct level. + $this->profile_visibility = new ProfileVisibility(); + } + + public function getVisibilityLevels() + { + return $this->profile_visibility->levels(); + } + + public function getVisibilityLevel() + { + return $this->profile_visibility->level(); + } + + public function restrictVisibilityTo($level) + { + $this->profile_visibility->setLevel($level); + } + + public function getVisibilityCondition($field) + { + return $field . ' IN ' . XDB::formatArray($this->getVisibilityLevels()); } private function buildQuery() { + // The root condition is built first because some orders need info + // available only once all UFC have set their conditions (UFO_Score) + if (is_null($this->query)) { + $where = $this->root->buildCondition($this); + $where = str_replace(array_keys($this->joinMetas), + $this->joinMetas, + $where); + } if (is_null($this->orderby)) { $orders = array(); foreach ($this->sort as $sort) { @@ -1576,13 +1743,12 @@ class UserFilter extends PlFilter } else { $this->orderby = 'ORDER BY ' . implode(', ', $orders); } + $this->orderby = str_replace(array_keys($this->joinMetas), + $this->joinMetas, + $this->orderby); } if (is_null($this->query)) { - $where = $this->root->buildCondition($this); - if ($this->with_forced_sn) { - $this->requireProfiles(); - $from = 'search_name AS sn'; - } else if ($this->with_accounts) { + if ($this->with_accounts) { $from = 'accounts AS a'; } else { $this->requireProfiles(); @@ -1595,21 +1761,57 @@ class UserFilter extends PlFilter } } + public function hasGroups() + { + return $this->grouper != null; + } + + public function getGroups() + { + return $this->getUIDGroups(); + } + + public function getUIDGroups() + { + $this->requireAccounts(); + $this->buildQuery(); + $token = $this->grouper->getGroupToken($this); + + $groups = XDB::rawFetchAllRow('SELECT ' . $token . ', COUNT(a.uid) + ' . $this->query . ' + GROUP BY ' . $token, + 0); + return $groups; + } + + public function getPIDGroups() + { + $this->requireProfiles(); + $this->buildQuery(); + $token = $this->grouper->getGroupToken($this); + + $groups = XDB::rawFetchAllRow('SELECT ' . $token . ', COUNT(p.pid) + ' . $this->query . ' + GROUP BY ' . $token, + 0); + return $groups; + } + private function getUIDList($uids = null, PlLimit &$limit) { $this->requireAccounts(); $this->buildQuery(); $lim = $limit->getSql(); $cond = ''; - if (!is_null($uids)) { + if (!empty($uids)) { $cond = XDB::format(' AND a.uid IN {?}', $uids); } - $fetched = XDB::fetchColumn('SELECT SQL_CALC_FOUND_ROWS a.uid - ' . $this->query . $cond . ' - GROUP BY a.uid - ' . $this->orderby . ' - ' . $lim); - $this->lastcount = (int)XDB::fetchOneCell('SELECT FOUND_ROWS()'); + $fetched = XDB::rawFetchColumn('SELECT SQL_CALC_FOUND_ROWS a.uid + ' . $this->query . $cond . ' + GROUP BY a.uid + ' . $this->orderby . ' + ' . $lim); + $this->lastusercount = (int)XDB::fetchOneCell('SELECT FOUND_ROWS()'); return $fetched; } @@ -1622,12 +1824,12 @@ class UserFilter extends PlFilter if (!is_null($pids)) { $cond = XDB::format(' AND p.pid IN {?}', $pids); } - $fetched = XDB::fetchColumn('SELECT SQL_CALC_FOUND_ROWS p.pid - ' . $this->query . $cond . ' - GROUP BY p.pid - ' . $this->orderby . ' - ' . $lim); - $this->lastcount = (int)XDB::fetchOneCell('SELECT FOUND_ROWS()'); + $fetched = XDB::rawFetchColumn('SELECT SQL_CALC_FOUND_ROWS p.pid + ' . $this->query . $cond . ' + GROUP BY p.pid + ' . $this->orderby . ' + ' . $lim); + $this->lastprofilecount = (int)XDB::fetchOneCell('SELECT FOUND_ROWS()'); return $fetched; } @@ -1645,8 +1847,9 @@ class UserFilter extends PlFilter { $this->requireAccounts(); $this->buildQuery(); - $count = (int)XDB::fetchOneCell('SELECT COUNT(*) - ' . $this->query . XDB::format(' AND a.uid = {?}', $user->id())); + $count = (int)XDB::rawFetchOneCell('SELECT COUNT(*) + ' . $this->query + . XDB::format(' AND a.uid = {?}', $user->id())); return $count == 1; } @@ -1656,8 +1859,9 @@ class UserFilter extends PlFilter { $this->requireProfiles(); $this->buildQuery(); - $count = (int)XDB::fetchOneCell('SELECT COUNT(*) - ' . $this->query . XDB::format(' AND p.pid = {?}', $profile->id())); + $count = (int)XDB::rawFetchOneCell('SELECT COUNT(*) + ' . $this->query + . XDB::format(' AND p.pid = {?}', $profile->id())); return $count == 1; } @@ -1728,7 +1932,7 @@ class UserFilter extends PlFilter public function getUID($pos = 0) { - $uids =$this->getUIDList(null, new PlFilter(1, $pos)); + $uids =$this->getUIDList(null, new PlLimit(1, $pos)); if (count($uids) == 0) { return null; } else { @@ -1744,7 +1948,7 @@ class UserFilter extends PlFilter public function getPID($pos = 0) { - $pids =$this->getPIDList(null, new PlFilter(1, $pos)); + $pids =$this->getPIDList(null, new PlLimit(1, $pos)); if (count($pids) == 0) { return null; } else { @@ -1772,24 +1976,24 @@ class UserFilter extends PlFilter return User::iterOverUIDs($this->getUIDs($limit)); } - public function getProfiles($limit = null) + public function getProfiles($limit = null, $fields = 0x0000, $visibility = null) { - return Profile::getBulkProfilesWithPIDs($this->getPIDs($limit)); + return Profile::getBulkProfilesWithPIDs($this->getPIDs($limit), $fields, $visibility); } - public function getProfile($pos = 0) + public function getProfile($pos = 0, $fields = 0x0000, $visibility = null) { $pid = $this->getPID($pos); if ($pid == null) { return null; } else { - return Profile::get($pid); + return Profile::get($pid, $fields, $visibility); } } - public function iterProfiles($limit = null) + public function iterProfiles($limit = null, $fields = 0x0000, $visibility = null) { - return Profile::iterOverPIDs($this->getPIDs($limit)); + return Profile::iterOverPIDs($this->getPIDs($limit), true, $fields, $visibility); } public function get($limit = null) @@ -1797,19 +2001,33 @@ class UserFilter extends PlFilter return $this->getUsers($limit); } + public function getTotalCount() { - if (is_null($this->lastcount)) { + return $this->getTotalUserCount(); + } + + public function getTotalUserCount() + { + if (is_null($this->lastusercount)) { + $this->requireAccounts(); $this->buildQuery(); - if ($this->with_accounts) { - $field = 'a.uid'; - } else { - $field = 'p.pid'; - } - return (int)XDB::fetchOneCell('SELECT COUNT(DISTINCT ' . $field . ') + return (int)XDB::rawFetchOneCell('SELECT COUNT(DISTINCT a.uid) ' . $this->query); } else { - return $this->lastcount; + return $this->lastusercount; + } + } + + public function getTotalProfileCount() + { + if (is_null($this->lastprofilecount)) { + $this->requireProfiles(); + $this->buildQuery(); + return (int)XDB::rawFetchOneCell('SELECT COUNT(DISTINCT p.pid) + ' . $this->query); + } else { + return $this->lastprofilecount; } } @@ -1821,6 +2039,10 @@ class UserFilter extends PlFilter public function addSort(PlFilterOrder &$sort) { + if (count($this->sort) == 0 && $sort instanceof PlFilterGroupableOrder) + { + $this->grouper = $sort; + } $this->sort[] = $sort; $this->orderby = null; } @@ -1887,40 +2109,55 @@ class UserFilter extends PlFilter */ private $with_profiles = false; private $with_accounts = false; - private $with_forced_sn = false; public function requireAccounts() { $this->with_accounts = true; } + public function accountsRequired() + { + return $this->with_accounts; + } + public function requireProfiles() { $this->with_profiles = true; } - /** Forces the "FROM" to use search_name instead of accounts or profiles */ - public function forceSearchName() + public function profilesRequired() { - $this->with_forced_sn = true; + return $this->with_profiles; } protected function accountJoins() { $joins = array(); - /** Quick search is much more efficient with sn first and PID second */ - if ($this->with_forced_sn) { - $joins['p'] = PlSqlJoin::left('profiles', '$PID = sn.pid'); - if ($this->with_accounts) { - $joins['ap'] = PlSqlJoin::left('account_profiles', '$ME.pid = $PID'); - $joins['a'] = PlSqlJoin::left('accounts', '$UID = ap.uid'); - } - } else if ($this->with_profiles && $this->with_accounts) { + if ($this->with_profiles && $this->with_accounts) { $joins['ap'] = PlSqlJoin::left('account_profiles', '$ME.uid = $UID AND FIND_IN_SET(\'owner\', ap.perms)'); $joins['p'] = PlSqlJoin::left('profiles', '$PID = ap.pid'); } return $joins; } + /** PERMISSIONS + */ + private $at = false; + public function requirePerms() + { + $this->requireAccounts(); + $this->at = true; + return 'at'; + } + + protected function permJoins() + { + if ($this->at) { + return array('at' => PlSqlJoin::left('account_types', '$ME.type = a.type')); + } else { + return array(); + } + } + /** DISPLAY */ const DISPLAY = 'display'; @@ -2000,26 +2237,31 @@ class UserFilter extends PlFilter /** NAMETOKENS */ - private $with_sn = false; - // Set $doingQuickSearch to true if you wish to optimize the query - public function addNameTokensFilter($doingQuickSearch = false) + private $name_tokens = array(); + private $nb_tokens = 0; + + public function addNameTokensFilter($token) { $this->requireProfiles(); - $this->with_forced_sn = ($this->with_forced_sn || $doingQuickSearch); - $this->with_sn = true; - return 'sn'; + $sub = 'sn' . (1 + $this->nb_tokens); + $this->nb_tokens++; + $this->name_tokens[$sub] = $token; + return $sub; } protected function nameTokensJoins() { /* We don't return joins, since with_sn forces the SELECT to run on search_name first */ - if ($this->with_sn && !$this->with_forced_sn) { - return array( - 'sn' => PlSqlJoin::left('search_name', '$ME.pid = $PID') - ); - } else { - return array(); + $joins = array(); + foreach ($this->name_tokens as $sub => $token) { + $joins[$sub] = PlSqlJoin::left('search_name', '$ME.pid = $PID'); } + return $joins; + } + + public function getNameTokens() + { + return $this->name_tokens; } /** NATIONALITY @@ -2048,7 +2290,7 @@ class UserFilter extends PlFilter const GRADE_MST = 'M%'; static public function isGrade($grade) { - return $grade == self::GRADE_ING || $grade == self::GRADE_PHD || $grade == self::GRADE_MST; + return ($grade !== 0) && ($grade == self::GRADE_ING || $grade == self::GRADE_PHD || $grade == self::GRADE_MST); } static public function assertGrade($grade) @@ -2207,8 +2449,8 @@ class UserFilter extends PlFilter if (!is_array($key)) { $key = array($key); } - $joins['e' . $sub] = PlSqlJoin::left('emails', '$ME.uid = $UID AND $ME.flags != \'filter\' - AND $ME.email IN {?}' . $key); + $joins['e' . $sub] = PlSqlJoin::left('emails', '$ME.uid = $UID AND $ME.flags != \'filter\' + AND $ME.email IN {?}', $key); } } foreach ($this->al as $sub=>$key) { @@ -2222,7 +2464,7 @@ class UserFilter extends PlFilter if (!is_array($key)) { $key = array($key); } - $joins['al' . $sub] = PlSqlJoin::left('aliases', '$ME.uid = $UID AND $ME.type IN (\'alias\', \'a_vie\') + $joins['al' . $sub] = PlSqlJoin::left('aliases', '$ME.uid = $UID AND $ME.type IN (\'alias\', \'a_vie\') AND $ME.alias IN {?}', $key); } } @@ -2335,30 +2577,18 @@ class UserFilter extends PlFilter /** JOBS */ - const JOB_SECTOR = 0x0001; - const JOB_SUBSECTOR = 0x0002; - const JOB_SUBSUBSECTOR = 0x0004; - const JOB_ALTERNATES = 0x0008; - const JOB_USERDEFINED = 0x0010; - const JOB_CV = 0x0020; - - const JOB_SECTORIZATION = 0x000F; - const JOB_ANY = 0x003F; + const JOB_USERDEFINED = 0x0001; + const JOB_CV = 0x0002; + const JOB_ANY = 0x0003; /** Joins : * pj => profile_job * pje => profile_job_enum - * pjse => profile_job_sector_enum - * pjsse => profile_job_subsector_enum - * pjssse => profile_job_subsubsector_enum - * pja => profile_job_alternates + * pjt => profile_job_terms */ - private $with_pj = false; + private $with_pj = false; private $with_pje = false; - private $with_pjse = false; - private $with_pjsse = false; - private $with_pjssse = false; - private $with_pja = false; + private $with_pjt = 0; public function addJobFilter() { @@ -2374,22 +2604,19 @@ class UserFilter extends PlFilter return 'pje'; } - public function addJobSectorizationFilter($type) + /** + * Adds a filter on job terms of profile. + * @param $nb the number of job terms to use + * @return an array of the fields to filter (one for each term). + */ + public function addJobTermsFilter($nb = 1) { - $this->addJobFilter(); - if ($type == self::JOB_SECTOR) { - $this->with_pjse = true; - return 'pjse'; - } else if ($type == self::JOB_SUBSECTOR) { - $this->with_pjsse = true; - return 'pjsse'; - } else if ($type == self::JOB_SUBSUBSECTOR) { - $this->with_pjssse = true; - return 'pjssse'; - } else if ($type == self::JOB_ALTERNATES) { - $this->with_pja = true; - return 'pja'; + $this->with_pjt = $nb; + $jobtermstable = array(); + for ($i = 1; $i <= $nb; ++$i) { + $jobtermstable[] = 'pjtr_'.$i; } + return $jobtermstable; } protected function jobJoins() @@ -2401,17 +2628,11 @@ class UserFilter extends PlFilter if ($this->with_pje) { $joins['pje'] = PlSqlJoin::left('profile_job_enum', '$ME.id = pj.jobid'); } - if ($this->with_pjse) { - $joins['pjse'] = PlSqlJoin::left('profile_job_sector_enum', '$ME.id = pj.sectorid'); - } - if ($this->with_pjsse) { - $joins['pjsse'] = PlSqlJoin::left('profile_job_subsector_enum', '$ME.id = pj.subsectorid'); - } - if ($this->with_pjssse) { - $joins['pjssse'] = PlSqlJoin::left('profile_job_subsubsector_enum', '$ME.id = pj.subsubsectorid'); - } - if ($this->with_pja) { - $joins['pja'] = PlSqlJoin::left('profile_job_alternates', '$ME.subsubsectorid = pj.subsubsectorid'); + if ($this->with_pjt > 0) { + for ($i = 1; $i <= $this->with_pjt; ++$i) { + $joins['pjt_'.$i] = PlSqlJoin::left('profile_job_term', '$ME.pid = $PID'); + $joins['pjtr_'.$i] = PlSqlJoin::left('profile_job_term_relation', '$ME.jtid_2 = pjt_'.$i.'.jtid'); + } } return $joins; } @@ -2481,23 +2702,29 @@ class UserFilter extends PlFilter */ private $pms = array(); - const MENTOR_EXPERTISE = 1; - const MENTOR_COUNTRY = 2; - const MENTOR_SECTOR = 3; + private $mjtr = false; + const MENTOR = 1; + const MENTOR_EXPERTISE = 2; + const MENTOR_COUNTRY = 3; + const MENTOR_TERM = 4; public function addMentorFilter($type) { $this->requireAccounts(); switch($type) { + case self::MENTOR: + $this->pms['pm'] = 'profile_mentor'; + return 'pm'; case self::MENTOR_EXPERTISE: $this->pms['pme'] = 'profile_mentor'; return 'pme'; case self::MENTOR_COUNTRY: $this->pms['pmc'] = 'profile_mentor_country'; return 'pmc'; - case self::MENTOR_SECTOR: - $this->pms['pms'] = 'profile_mentor_sector'; - return 'pms'; + case self::MENTOR_TERM: + $this->pms['pmt'] = 'profile_mentor_term'; + $this->mjtr = true; + return 'mjtr'; default: Platal::page()->killError("Undefined mentor filter."); } @@ -2509,6 +2736,9 @@ class UserFilter extends PlFilter foreach ($this->pms as $sub => $tab) { $joins[$sub] = PlSqlJoin::left($tab, '$ME.pid = $PID'); } + if ($this->mjtr) { + $joins['mjtr'] = PlSqlJoin::left('profile_job_term_relation', '$ME.jtid_2 = pmt.jtid'); + } return $joins; } @@ -2593,6 +2823,26 @@ class UserFilter extends PlFilter } + /** PHOTOS + */ + private $with_photo; + public function addPhotoFilter() + { + $this->requireProfiles(); + $this->with_photo = true; + return 'photo'; + } + + protected function photoJoins() + { + if ($this->with_photo) { + return array('photo' => PlSqlJoin::left('profile_photos', '$ME.pid = $PID')); + } else { + return array(); + } + } + + /** MARKETING */ private $with_rm; @@ -2620,6 +2870,21 @@ class ProfileFilter extends UserFilter { return $this->getProfiles($limit); } + + public function filter(array $profiles, $limit = null) + { + return $this->filterProfiles($profiles, self::defaultLimit($limit)); + } + + public function getTotalCount() + { + return $this->getTotalProfileCount(); + } + + public function getGroups() + { + return $this->getPIDGroups(); + } } // }}}