X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=classes%2Fuserfilter.php;h=dffa8a7191f6dd15eb02152f156457825c378bcd;hb=fda78a6777a5f385537fc9f291afb310e12525e5;hp=db2111b68b8daa696c821c8c8d379d2e6d0e5740;hpb=4a93c3a36613323a86c9362688b226103ef15203;p=platal.git diff --git a/classes/userfilter.php b/classes/userfilter.php index db2111b..dffa8a7 100644 --- a/classes/userfilter.php +++ b/classes/userfilter.php @@ -1,6 +1,6 @@ addWheteverFilter so that the UserFilter makes - * adequate joins. It must return the 'WHERE' condition to use - * with the filter. - */ -interface UserFilterCondition extends PlFilterCondition -{ -} -// }}} - -// {{{ class UFC_Profile -/** Filters users who have a profile - */ -class UFC_Profile implements UserFilterCondition -{ - public function buildCondition(PlFilter &$uf) - { - return '$PID IS NOT NULL'; - } -} -// }}} - -// {{{ class UFC_Hruid -/** Filters users based on their hruid - * @param $val Either an hruid, or a list of those - */ -class UFC_Hruid implements UserFilterCondition -{ - private $hruids; - - public function __construct($val) - { - if (!is_array($val)) { - $val = array($val); - } - $this->hruids = $val; - } - - public function buildCondition(PlFilter &$uf) - { - $ufc->requireAccounts(); - - return 'a.hruid IN ' . XDB::formatArray($this->hruids); - } -} -// }}} - -// {{{ class UFC_Comment -class UFC_Comment implements UserFilterCondition -{ - private $text; - - public function __construct($text) - { - $this->text = $text; - } - - public function buildCondition(PlFilter &$uf) - { - $uf->requireProfiles(); - return 'p.freetext LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->text); - } -} -// }}} - -// {{{ class UFC_Promo -/** Filters users based on promotion - * @param $comparison Comparison operator (>, =, ...) - * @param $grade Formation on which to restrict, UserFilter::DISPLAY for "any formation" - * @param $promo Promotion on which the filter is based - */ -class UFC_Promo implements UserFilterCondition -{ - - private $grade; - private $promo; - private $comparison; - - public function __construct($comparison, $grade, $promo) - { - $this->grade = $grade; - $this->comparison = $comparison; - $this->promo = $promo; - if ($this->grade != UserFilter::DISPLAY) { - UserFilter::assertGrade($this->grade); - } - } - - public function buildCondition(PlFilter &$uf) - { - if ($this->grade == UserFilter::DISPLAY) { - $sub = $uf->addDisplayFilter(); - return XDB::format('pd' . $sub . '.promo ' . $this->comparison . ' {?}', $this->promo); - } else { - $sub = $uf->addEducationFilter(true, $this->grade); - $field = 'pe' . $sub . '.' . UserFilter::promoYear($this->grade); - return $field . ' IS NOT NULL AND ' . $field . ' ' . $this->comparison . ' ' . XDB::format('{?}', $this->promo); - } - } -} -// }}} - -// {{{ class UFC_EducationSchool -/** Filters users by formation - * @param $val The formation to search (either ID or array of IDs) - */ -class UFC_EducationSchool implements UserFilterCondition -{ - private $val; - - public function __construct($val) - { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addEducationFilter(); - return 'pe' . $sub . '.eduid IN ' . XDB::formatArray($this->val); - } -} -// }}} - -// {{{ class UFC_EducationDegree -class UFC_EducationDegree implements UserFilterCondition -{ - private $diploma; - - public function __construct($diploma) - { - if (! is_array($diploma)) { - $diploma = array($diploma); - } - $this->diploma = $diploma; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addEducationFilter(); - return 'pee' . $sub . '.degreeid IN ' . XDB::formatArray($this->val); - } -} -// }}} - -// {{{ class UFC_EducationField -class UFC_EducationField implements UserFilterCondition -{ - private $val; - - public function __construct($val) - { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addEducationFilter(); - return 'pee' . $sub . '.fieldid IN ' . XDB::formatArray($this->val); - } -} -// }}} - -// {{{ class UFC_Name -/** Filters users based on name - * @param $type Type of name field on which filtering is done (firstname, lastname...) - * @param $text Text on which to filter - * @param $mode Flag indicating search type (prefix, suffix, with particule...) - */ -class UFC_Name implements UserFilterCondition -{ - const PREFIX = 1; - const SUFFIX = 2; - const PARTICLE = 7; - const VARIANTS = 8; - const CONTAINS = 3; - - private $type; - private $text; - private $mode; - - public function __construct($type, $text, $mode) - { - $this->type = $type; - $this->text = $text; - $this->mode = $mode; - } - - private function buildNameQuery($type, $variant, $where, UserFilter &$uf) - { - $sub = $uf->addNameFilter($type, $variant); - return str_replace('$ME', 'pn' . $sub, $where); - } - - public function buildCondition(PlFilter &$uf) - { - $left = '$ME.name'; - $op = ' LIKE '; - if (($this->mode & self::PARTICLE) == self::PARTICLE) { - $left = 'CONCAT($ME.particle, \' \', $ME.name)'; - } - if (($this->mode & self::CONTAINS) == 0) { - $right = XDB::format('{?}', $this->text); - $op = ' = '; - } else if (($this->mode & self::CONTAINS) == self::PREFIX) { - $right = XDB::format('CONCAT({?}, \'%\')', $this->text); - } else if (($this->mode & self::CONTAINS) == self::SUFFIX) { - $right = XDB::format('CONCAT(\'%\', {?})', $this->text); - } else { - $right = XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->text); - } - $cond = $left . $op . $right; - $conds = array($this->buildNameQuery($this->type, null, $cond, $uf)); - if (($this->mode & self::VARIANTS) != 0 && isset(UserFilter::$name_variants[$this->type])) { - foreach (UserFilter::$name_variants[$this->type] as $var) { - $conds[] = $this->buildNameQuery($this->type, $var, $cond, $uf); - } - } - return implode(' OR ', $conds); - } -} -// }}} - -// {{{ class UFC_NameTokens -/** Selects users based on tokens in their name (for quicksearch) - * @param $tokens An array of tokens to search - * @param $flags Flags the tokens must have (e.g 'public' for public search) - * @param $soundex (bool) Whether those tokens are fulltext or soundex - */ -class UFC_NameTokens implements UserFilterCondition -{ - /* Flags */ - const FLAG_PUBLIC = 'public'; - - private $tokens; - private $flags; - private $soundex; - private $exact; - - public function __construct($tokens, $flags = array(), $soundex = false, $exact = false) - { - $this->tokens = $tokens; - if (is_array($flags)) { - $this->flags = $flags; - } else { - $this->flags = array($flags); - } - $this->soundex = $soundex; - $this->exact = $exact; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addNameTokensFilter(!($this->exact || $this->soundex)); - $conds = array(); - if ($this->soundex) { - $conds[] = $sub . '.soundex IN ' . XDB::formatArray($this->tokens); - } else if ($this->exact) { - $conds[] = $sub . '.token IN ' . XDB::formatArray($this->tokens); - } else { - $tokconds = array(); - foreach ($this->tokens as $token) { - $tokconds[] = $sub . '.token LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $token); - } - $conds[] = implode(' OR ', $tokconds); - } - - if ($this->flags != null) { - $conds[] = $sub . '.flags IN ' . XDB::formatArray($this->flags); - } - - return implode(' AND ', $conds); - } -} -// }}} - -// {{{ class UFC_Nationality -class UFC_Nationality implements UserFilterCondition -{ - private $val; - - public function __construct($val) - { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; - } - - public function buildCondition(PlFilter &$uf) - { - $uf->requireProfiles(); - $nat = XDB::formatArray($this->val); - $conds = array( - 'p.nationality1 IN ' . $nat, - 'p.nationality2 IN ' . $nat, - 'p.nationality3 IN ' . $nat, - ); - return implode(' OR ', $conds); - } -} -// }}} - -// {{{ class UFC_Dead -/** Filters users based on death date - * @param $comparison Comparison operator - * @param $date Date to which death date should be compared - */ -class UFC_Dead implements UserFilterCondition -{ - private $comparison; - private $date; - - public function __construct($comparison = null, $date = null) - { - $this->comparison = $comparison; - $this->date = $date; - } - - public function buildCondition(PlFilter &$uf) - { - $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)); - } - return $str; - } -} -// }}} - -// {{{ class UFC_Registered -/** Filters users based on registration state - * @param $active Whether we want to use only "active" users (i.e with a valid redirection) - * @param $comparison Comparison operator - * @param $date Date to which users registration date should be compared - */ -class UFC_Registered implements UserFilterCondition -{ - private $active; - private $comparison; - private $date; - - public function __construct($active = false, $comparison = null, $date = null) - { - $this->active = $active; - $this->comparison = $comparison; - $this->date = $date; - } - - public function buildCondition(PlFilter &$uf) - { - $uf->requireAccounts(); - if ($this->active) { - $date = 'a.uid IS NOT NULL AND a.state = \'active\''; - } else { - $date = 'a.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)); - } - return $date; - } -} -// }}} - -// {{{ class UFC_ProfileUpdated -/** Filters users based on profile update date - * @param $comparison Comparison operator - * @param $date Date to which profile update date must be compared - */ -class UFC_ProfileUpdated implements UserFilterCondition -{ - private $comparison; - private $date; - - public function __construct($comparison = null, $date = null) - { - $this->comparison = $comparison; - $this->date = $date; - } - - public function buildCondition(PlFilter &$uf) - { - $uf->requireProfiles(); - return 'p.last_change ' . $this->comparison . XDB::format(' {?}', date('Y-m-d H:i:s', $this->date)); - } -} -// }}} - -// {{{ class UFC_Birthday -/** Filters users based on next birthday date - * @param $comparison Comparison operator - * @param $date Date to which users next birthday date should be compared - */ -class UFC_Birthday implements UserFilterCondition -{ - private $comparison; - private $date; - - public function __construct($comparison = null, $date = null) - { - $this->comparison = $comparison; - $this->date = $date; - } - - public function buildCondition(PlFilter &$uf) - { - $uf->requireProfiles(); - return 'p.next_birthday ' . $this->comparison . XDB::format(' {?}', date('Y-m-d', $this->date)); - } -} -// }}} - -// {{{ class UFC_Sex -/** Filters users based on sex - * @parm $sex One of User::GENDER_MALE or User::GENDER_FEMALE, for selecting users - */ -class UFC_Sex implements UserFilterCondition -{ - private $sex; - public function __construct($sex) - { - $this->sex = $sex; - } - - public function buildCondition(PlFilter &$uf) - { - if ($this->sex != User::GENDER_MALE && $this->sex != User::GENDER_FEMALE) { - return self::COND_FALSE; - } else { - $uf->requireProfiles(); - return XDB::format('p.sex = {?}', $this->sex == User::GENDER_FEMALE ? 'female' : 'male'); - } - } -} -// }}} - -// {{{ class UFC_Group -/** Filters users based on group membership - * @param $group Group whose members we are selecting - * @param $anim Whether to restrict selection to animators of that group - */ -class UFC_Group implements UserFilterCondition -{ - private $group; - private $anim; - public function __construct($group, $anim = false) - { - $this->group = $group; - $this->anim = $anim; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addGroupFilter($this->group); - $where = 'gpm' . $sub . '.perms IS NOT NULL'; - if ($this->anim) { - $where .= ' AND gpm' . $sub . '.perms = \'admin\''; - } - return $where; - } -} -// }}} - -// {{{ class UFC_Binet -/** Selects users based on their belonging to a given (list of) binet - * @param $binet either a binet_id or an array of binet_ids - */ -class UFC_Binet implements UserFilterCondition -{ - private $val; - - public function __construct($val) - { - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addBinetsFilter(); - return $sub . '.binet_id IN ' . XDB::formatArray($this->val); - } -} -// }}} - -// {{{ class UFC_Section -/** Selects users based on section - * @param $section ID of the section - */ -class UFC_Section implements UserFilterCondition -{ - private $section; - - public function __construct($section) - { - $this->section = $section; - } - - public function buildCondition(PlFilter &$uf) - { - $uf->requireProfiles(); - return 'p.section = ' . XDB::format('{?}', $this->section); - } -} -// }}} - -// {{{ class UFC_Email -/** Filters users based on email address - * @param $email Email whose owner we are looking for - */ -class UFC_Email implements UserFilterCondition -{ - private $email; - public function __construct($email) - { - $this->email = $email; - } - - public function buildCondition(PlFilter &$uf) - { - if (User::isForeignEmailAddress($this->email)) { - $sub = $uf->addEmailRedirectFilter($this->email); - return XDB::format('e' . $sub . '.email IS NOT NULL OR a.email = {?}', $this->email); - } else if (User::isVirtualEmailAddress($this->email)) { - $sub = $uf->addVirtualEmailFilter($this->email); - return 'vr' . $sub . '.redirect IS NOT NULL'; - } else { - @list($user, $domain) = explode('@', $this->email); - $sub = $uf->addAliasFilter($user); - return 'al' . $sub . '.alias IS NOT NULL'; - } - } -} -// }}} - -// {{{ class UFC_EmailList -/** Filters users based on an email list - * @param $emails List of emails whose owner must be selected - */ -class UFC_EmailList implements UserFilterCondition -{ - private $emails; - public function __construct($emails) - { - $this->emails = $emails; - } - - public function buildCondition(PlFilter &$uf) - { - $email = null; - $virtual = null; - $alias = null; - $cond = array(); - - if (count($this->emails) == 0) { - return PlFilterCondition::COND_TRUE; - } - - foreach ($this->emails as $entry) { - if (User::isForeignEmailAddress($entry)) { - if (is_null($email)) { - $email = $uf->addEmailRedirectFilter(); - } - $cond[] = XDB::format('e' . $email . '.email = {?} OR a.email = {?}', $entry, $entry); - } else if (User::isVirtualEmailAddress($entry)) { - if (is_null($virtual)) { - $virtual = $uf->addVirtualEmailFilter(); - } - $cond[] = XDB::format('vr' . $virtual . '.redirect IS NOT NULL AND v' . $virtual . '.alias = {?}', $entry); - } else { - if (is_null($alias)) { - $alias = $uf->addAliasFilter(); - } - @list($user, $domain) = explode('@', $entry); - $cond[] = XDB::format('al' . $alias . '.alias = {?}', $user); - } - } - return '(' . implode(') OR (', $cond) . ')'; - } -} -// }}} - -// {{{ class UFC_Address -abstract class UFC_Address implements UserFilterCondition -{ - /** Valid address type ('hq' is reserved for company addresses) - */ - const TYPE_HOME = 1; - const TYPE_PRO = 2; - const TYPE_ANY = 3; - - /** Text for these types - */ - protected static $typetexts = array( - self::TYPE_HOME => 'home', - self::TYPE_PRO => 'pro', - ); - - protected $type; - - /** Flags for addresses - */ - const FLAG_CURRENT = 0x0001; - const FLAG_TEMP = 0x0002; - const FLAG_SECOND = 0x0004; - const FLAG_MAIL = 0x0008; - const FLAG_CEDEX = 0x0010; - - // Binary OR of those flags - const FLAG_ANY = 0x001F; - - /** Text of these flags - */ - protected static $flagtexts = array( - self::FLAG_CURRENT => 'current', - self::FLAG_TEMP => 'temporary', - self::FLAG_SECOND => 'secondary', - self::FLAG_MAIL => 'mail', - self::FLAG_CEDEX => 'cedex', - ); - - protected $flags; - - public function __construct($type = null, $flags = null) - { - $this->type = $type; - $this->flags = $flags; - } - - protected function initConds($sub) - { - $conds = array(); - $types = array(); - foreach (self::$typetexts as $flag => $type) { - if ($flag & $this->type) { - $types[] = $type; - } - } - if (count($types)) { - $conds[] = $sub . '.type IN ' . XDB::formatArray($types); - } - - if ($this->flags != self::FLAG_ANY) { - foreach(self::$flagtexts as $flag => $text) { - if ($flag & $this->flags) { - $conds[] = 'FIND_IN_SET(' . XDB::format('{?}', $text) . ', ' . $sub . '.flags)'; - } - } - } - return $conds; - } - -} -// }}} - -// {{{ class UFC_AddressText -/** Select users based on their address, using full text search - * @param $text Text for filter in fulltext search - * @param $textSearchMode Mode for search (PREFIX, SUFFIX, ...) - * @param $type Filter on address type - * @param $flags Filter on address flags - * @param $country Filter on address country - * @param $locality Filter on address locality - */ -class UFC_AddressText extends UFC_Address -{ - /** Flags for text search - */ - const PREFIX = 0x0001; - const SUFFIX = 0x0002; - const CONTAINS = 0x0003; - - private $text; - private $textSearchMode; - - public function __construct($text = null, $textSearchMode = self::CONTAINS, - $type = null, $flags = self::FLAG_ANY, $country = null, $locality = null) - { - parent::__construct($type, $flags); - $this->text = $text; - $this->textSearchMode = $textSearchMode; - $this->country = $country; - $this->locality = $locality; - } - - private function mkMatch($txt) - { - $op = ' LIKE '; - if (($this->textSearchMode & self::CONTAINS) == 0) { - $right = XDB::format('{?}', $this->text); - $op = ' = '; - } else if (($this->mode & self::CONTAINS) == self::PREFIX) { - $right = XDB::format('CONCAT({?}, \'%\')', $this->text); - } else if (($this->mode & self::CONTAINS) == self::SUFFIX) { - $right = XDB::format('CONCAT(\'%\', {?})', $this->text); - } else { - $right = XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->text); - } - return $op . $right; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addAddressFilter(); - $conds = $this->initConds($sub); - if ($this->text != null) { - $conds[] = $sub . '.text' . $this->mkMatch($this->text); - } - - if ($this->country != null) { - $subc = $uf->addAddressCountryFilter(); - $subconds = array(); - $subconds[] = $subc . '.country' . $this->mkMatch($this->country); - $subconds[] = $subc . '.countryFR' . $this->mkMatch($this->country); - $conds[] = implode(' OR ', $subconds); - } - - if ($this->locality != null) { - $subl = $uf->addAddressLocalityFilter(); - $conds[] = $subl . '.name' . $this->mkMatch($this->locality); - } - - return implode(' AND ', $conds); - } -} -// }}} - -// {{{ class UFC_AddressField -/** Filters users based on their address, - * @param $val Either a code for one of the fields, or an array of such codes - * @param $fieldtype The type of field to look for - * @param $type Filter on address type - * @param $flags Filter on address flags - */ -class UFC_AddressField extends UFC_Address -{ - const FIELD_COUNTRY = 1; - const FIELD_ADMAREA = 2; - const FIELD_SUBADMAREA = 3; - const FIELD_LOCALITY = 4; - const FIELD_ZIPCODE = 5; - - /** Data of the filter - */ - private $val; - private $fieldtype; - - public function __construct($val, $fieldtype, $type = null, $flags = self::FLAG_ANY) - { - parent::__construct($type, $flags); - - if (!is_array($val)) { - $val = array($val); - } - $this->val = $val; - $this->fieldtype = $fieldtype; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addAddressFilter(); - $conds = $this->initConds($sub); - - switch ($this->fieldtype) { - case self::FIELD_COUNTRY: - $field = 'countryId'; - break; - case self::FIELD_ADMAREA: - $field = 'administrativeAreaId'; - break; - case self::FIELD_SUBADMAREA: - $field = 'subAdministrativeAreaId'; - break; - case self::FIELD_LOCALITY: - $field = 'localityId'; - break; - case self::FIELD_ZIPCODE: - $field = 'postalCode'; - break; - default: - Platal::page()->killError('Invalid address field type: ' . $this->fieldtype); - } - $conds[] = $sub . '.' . $field . ' IN ' . XDB::formatArray($this->val); - - return implode(' AND ', $conds); - } -} -// }}} - -// {{{ class UFC_Corps -/** Filters users based on the corps they belong to - * @param $corps Corps we are looking for (abbreviation) - * @param $type Whether we search for original or current corps - */ -class UFC_Corps implements UserFilterCondition -{ - const CURRENT = 1; - const ORIGIN = 2; - - private $corps; - private $type; - - public function __construct($corps, $type = self::CURRENT) - { - $this->corps = $corps; - $this->type = $type; - } - - public function buildCondition(PlFilter &$uf) - { - /** Tables shortcuts: - * pc for profile_corps, - * pceo for profile_corps_enum - orginal - * pcec for profile_corps_enum - current - */ - $sub = $uf->addCorpsFilter($this->type); - $cond = $sub . '.abbreviation = ' . $corps; - return $cond; - } -} -// }}} - -// {{{ class UFC_Corps_Rank -/** Filters users based on their rank in the corps - * @param $rank Rank we are looking for (abbreviation) - */ -class UFC_Corps_Rank implements UserFilterCondition -{ - private $rank; - public function __construct($rank) - { - $this->rank = $rank; - } - - public function buildCondition(PlFilter &$uf) - { - /** Tables shortcuts: - * pcr for profile_corps_rank - */ - $sub = $uf->addCorpsRankFilter(); - $cond = $sub . '.abbreviation = ' . $rank; - return $cond; - } -} -// }}} - -// {{{ class UFC_Job_Company -/** Filters users based on the company they belong to - * @param $type The field being searched (self::JOBID, self::JOBNAME or self::JOBACRONYM) - * @param $value The searched value - */ -class UFC_Job_Company implements UserFilterCondition -{ - const JOBID = 'id'; - const JOBNAME = 'name'; - const JOBACRONYM = 'acronym'; - - private $type; - private $value; - - public function __construct($type, $value) - { - $this->assertType($type); - $this->type = $type; - $this->value = $value; - } - - private function assertType($type) - { - if ($type != self::JOBID && $type != self::JOBNAME && $type != self::JOBACRONYM) { - Platal::page()->killError("Type de recherche non valide."); - } - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addJobCompanyFilter(); - $cond = $sub . '.' . $this->type . ' = ' . XDB::format('{?}', $this->value); - 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_Sectorization implements UserFilterCondition -{ - private $val; - private $type; - - public function __construct($val, $type = UserFilter::JOB_SECTOR) - { - 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); - } -} -// }}} - -// {{{ 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) - */ -class UFC_Job_Description implements UserFilterCondition -{ - - private $description; - private $fields; - - public function __construct($description, $fields) - { - $this->fields = $fields; - $this->description = $description; - } - - public function buildCondition(PlFilter &$uf) - { - $conds = array(); - if ($this->fields & UserFilter::JOB_USERDEFINED) { - $sub = $uf->addJobFilter(); - $conds[] = $sub . '.description LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->description); - } - if ($this->fields & UserFilter::JOB_CV) { - $uf->requireProfiles(); - $conds[] = 'p.cv LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->description); - } - if ($this->fields & UserFilter::JOB_SECTOR) { - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SECTOR); - $conds[] = $sub . '.name LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->description); - } - if ($this->fields & UserFilter::JOB_SUBSECTOR) { - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SUBSECTOR); - $conds[] = $sub . '.name LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->description); - } - if ($this->fields & UserFilter::JOB_SUBSUBSECTOR) { - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SUBSUBSECTOR); - $conds[] = $sub . '.name LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->description); - $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_ALTERNATES); - $conds[] = $sub . '.name LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->description); - } - return implode(' OR ', $conds); - } -} -// }}} - -// {{{ class UFC_Networking -/** Filters users based on network identity (IRC, ...) - * @param $type Type of network (-1 for any) - * @param $value Value to search - */ -class UFC_Networking implements UserFilterCondition -{ - private $type; - private $value; - - public function __construct($type, $value) - { - $this->type = $type; - $this->value = $value; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addNetworkingFilter(); - $conds = array(); - $conds[] = $sub . '.address = ' . XDB::format('CONCAT(\'%\', {?}, \'%\')', $this->value); - if ($this->type != -1) { - $conds[] = $sub . '.network_type = ' . XDB::format('{?}', $this->type); - } - return implode(' AND ', $conds); - } -} -// }}} - -// {{{ class UFC_Phone -/** Filters users based on their phone number - * @param $num_type Type of number (pro/user/home) - * @param $phone_type Type of phone (fixed/mobile/fax) - * @param $number Phone number - */ -class UFC_Phone implements UserFilterCondition -{ - const NUM_PRO = 'pro'; - const NUM_USER = 'user'; - const NUM_HOME = 'address'; - const NUM_ANY = 'any'; - - const PHONE_FIXED = 'fixed'; - const PHONE_MOBILE = 'mobile'; - const PHONE_FAX = 'fax'; - const PHONE_ANY = 'any'; - - private $num_type; - private $phone_type; - private $number; - - public function __construct($number, $num_type = self::NUM_ANY, $phone_type = self::PHONE_ANY) - { - require_once('profil.func.inc.php'); - $this->number = $number; - $this->num_type = $num_type; - $this->phone_type = format_phone_number($phone_type); - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addPhoneFilter(); - $conds = array(); - $conds[] = $sub . '.search_tel = ' . XDB::format('{?}', $this->number); - if ($this->num_type != self::NUM_ANY) { - $conds[] = $sub . '.link_type = ' . XDB::format('{?}', $this->num_type); - } - if ($this->phone_type != self::PHONE_ANY) { - $conds[] = $sub . '.tel_type = ' . XDB::format('{?}', $this->phone_type); - } - return implode(' AND ', $conds); - } -} -// }}} - -// {{{ class UFC_Medal -/** Filters users based on their medals - * @param $medal ID of the medal - * @param $grade Grade of the medal (null for 'any') - */ -class UFC_Medal implements UserFilterCondition -{ - private $medal; - private $grade; - - public function __construct($medal, $grade = null) - { - $this->medal = $medal; - $this->grade = $grade; - } - - public function buildCondition(PlFilter &$uf) - { - $conds = array(); - $sub = $uf->addMedalFilter(); - $conds[] = $sub . '.mid = ' . XDB::format('{?}', $this->medal); - if ($this->grade != null) { - $conds[] = $sub . '.gid = ' . XDB::format('{?}', $this->grade); - } - return implode(' AND ', $conds); - } -} -// }}} - -// {{{ class UFC_Mentor_Expertise -/** Filters users by mentoring expertise - * @param $expertise Domain of expertise - */ -class UFC_Mentor_Expertise implements UserFilterCondition -{ - private $expertise; - - public function __construct($expertise) - { - $this->expertise = $expertise; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addMentorFilter(UserFilter::MENTOR_EXPERTISE); - return $sub . '.expertise LIKE ' . XDB::format('CONCAT(\'%\', {?}, \'%\'', $this->expertise); - } -} -// }}} - -// {{{ class UFC_Mentor_Country -/** Filters users by mentoring country - * @param $country Two-letters code of country being searched - */ -class UFC_Mentor_Country implements UserFilterCondition -{ - private $country; - - public function __construct($country) - { - $this->country = $country; - } - - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addMentorFilter(UserFilter::MENTOR_COUNTRY); - return $sub . '.country = ' . 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_Sectorization implements UserFilterCondition -{ - const SECTOR = 1; - const SUBSECTOR = 2; - private $sector; - private $type; - - public function __construct($sector, $type = self::SECTOR) - { - $this->sector = $sector; - $this->type = $type; - } - - 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); - } -} -// }}} - -// {{{ class UFC_UserRelated -/** Filters users based on a relation toward a user - * @param $user User to which searched users are related - */ -abstract class UFC_UserRelated implements UserFilterCondition -{ - protected $user; - public function __construct(PlUser &$user) - { - $this->user =& $user; - } -} -// }}} - -// {{{ class UFC_Contact -/** Filters users who belong to selected user's contacts - */ -class UFC_Contact extends UFC_UserRelated -{ - public function buildCondition(PlFilter &$uf) - { - $sub = $uf->addContactFilter($this->user->id()); - return 'c' . $sub . '.contact IS NOT NULL'; - } -} -// }}} - -// {{{ class UFC_WatchRegistration -/** Filters users being watched by selected user - */ -class UFC_WatchRegistration extends UFC_UserRelated -{ - public function buildCondition(PlFilter &$uf) - { - if (!$this->user->watch('registration')) { - return PlFilterCondition::COND_FALSE; - } - $uids = $this->user->watchUsers(); - if (count($uids) == 0) { - return PlFilterCondition::COND_FALSE; - } else { - return '$UID IN ' . XDB::formatArray($uids); - } - } -} -// }}} - -// {{{ class UFC_WatchPromo -/** Filters users belonging to a promo watched by selected user - * @param $user Selected user (the one watching promo) - * @param $grade Formation the user is watching - */ -class UFC_WatchPromo extends UFC_UserRelated -{ - private $grade; - public function __construct(PlUser &$user, $grade = UserFilter::GRADE_ING) - { - parent::__construct($user); - $this->grade = $grade; - } - - public function buildCondition(PlFilter &$uf) - { - $promos = $this->user->watchPromos(); - if (count($promos) == 0) { - return PlFilterCondition::COND_FALSE; - } else { - $sube = $uf->addEducationFilter(true, $this->grade); - $field = 'pe' . $sube . '.' . UserFilter::promoYear($this->grade); - return $field . ' IN ' . XDB::formatArray($promos); - } - } -} -// }}} - -// {{{ class UFC_WatchContact -/** Filters users watched by selected user - */ -class UFC_WatchContact extends UFC_Contact -{ - public function buildCondition(PlFilter &$uf) - { - if (!$this->user->watchContacts()) { - return PlFilterCondition::COND_FALSE; - } - return parent::buildCondition($uf); - } -} -// }}} - - -/****************** - * 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 -{ - private $grade; - - public function __construct($grade = null, $desc = false) - { - parent::__construct($desc); - $this->grade = $grade; - } - - protected function getSortTokens(PlFilter &$uf) - { - if (UserFilter::isGrade($this->grade)) { - $sub = $uf->addEducationFilter($this->grade); - return 'pe' . $sub . '.' . UserFilter::promoYear($this->grade); - } else { - $sub = $uf->addDisplayFilter(); - return 'pd' . $sub . '.promo'; - } - } -} -// }}} - -// {{{ class UFO_Name -/** Sorts users by name - * @param $type Type of name on which to sort (firstname...) - * @param $variant Variant of that name to use (marital, ordinary...) - * @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 -{ - private $type; - private $variant; - private $particle; - - public function __construct($type, $variant = null, $particle = false, $desc = false) - { - parent::__construct($desc); - $this->type = $type; - $this->variant = $variant; - $this->particle = $particle; - } - - protected function getSortTokens(PlFilter &$uf) - { - if (UserFilter::isDisplayName($this->type)) { - $sub = $uf->addDisplayFilter(); - return 'pd' . $sub . '.' . $this->type; - } else { - $sub = $uf->addNameFilter($this->type, $this->variant); - if ($this->particle) { - return 'CONCAT(pn' . $sub . '.particle, \' \', pn' . $sub . '.name)'; - } else { - return 'pn' . $sub . '.name'; - } - } - } -} -// }}} - -// {{{ class UFO_Score -class UFO_Score extends UserFilterOrder -{ - protected function getSortTokens(PlFilter &$uf) - { - $sub = $uf->addNameTokensFilter(); - return 'SUM(' . $sub . '.score)'; - } -} -// }}} - -// {{{ class UFO_Registration -/** Sorts users based on registration date - */ -class UFO_Registration extends UserFilterOrder -{ - protected function getSortTokens(PlFilter &$uf) - { - return 'a.registration_date'; - } -} -// }}} - -// {{{ class UFO_Birthday -/** Sorts users based on next birthday date - */ -class UFO_Birthday extends UserFilterOrder -{ - protected function getSortTokens(PlFilter &$uf) - { - return 'p.next_birthday'; - } -} -// }}} - -// {{{ class UFO_ProfileUpdate -/** Sorts users based on last profile update - */ -class UFO_ProfileUpdate extends UserFilterOrder -{ - protected function getSortTokens(PlFilter &$uf) - { - return 'p.last_change'; - } -} -// }}} - -// {{{ class UFO_Death -/** Sorts users based on death date - */ -class UFO_Death extends UserFilterOrder -{ - protected function getSortTokens(PlFilter &$uf) - { - return 'p.deathdate'; - } -} -// }}} - +require_once dirname(__FILE__) . '/userfilter/conditions.inc.php'; +require_once dirname(__FILE__) . '/userfilter/orders.inc.php'; /*********************************** ********************************* @@ -1447,7 +52,7 @@ class UFO_Death extends UserFilterOrder * in the final query. * * In the join_criter text, $ME is replaced with 'join_tablealias', $PID with - * profile.pid, and $UID with auth_user_md5.user_id. + * profile.pid, and $UID with accounts.uid. * * For each kind of "JOIN" needed, a function named addXXXFilter() should be defined; * its parameter will be used to set various private vars of the UserFilter describing @@ -1455,7 +60,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: @@ -1475,10 +80,17 @@ 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 $visibility = null; + // If the 'search' visibility should be based on a DB field instead. + private $visibility_field = null; + + private $lastusercount = null; + private $lastprofilecount = null; public function __construct($cond = null, $sort = null) { @@ -1497,7 +109,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) { @@ -1505,10 +117,64 @@ class UserFilter extends PlFilter } } } + + // This will set the visibility to the default correct level. + $this->visibility = Visibility::defaultForRead(); + } + + /** Get the SQL condition to filter by visibility level for a field. + * This will return a SQL condition which evaluates to True if the given + * field display level is available from the current access level. + * @param $field Name of the field holding a display level + * @return string SQL condition, properly escaped, for that field. + */ + public function getVisibilityConditionForField($field) + { + if ($this->visibility_field != null) { + // Use enum 'bit' arithmetic. + // Display levels are ordered as 'hidden, private, ax, public' + // Thus ax > private. + // The $sub.display_level cell will contain the 'most private' display + // level available based on $field. If it is 'ax' and $field is + // 'private','ax' <= 'private' is false. + $sub = $this->addVisibilityFieldFilter($this->visibility_field); + return $sub . '.best_display_level + 0 <= 0 + ' . $field; + } else { + $sub = $this->addVisibilityAbsoluteFilter($this->visibility->level()); + return $sub . '.best_display_level + 0 <= 0 + ' . $field; + } + } + + /** Get the SQL condition to filter by a given visibility level. + * @param $level One of Visibility::EXPORT_* + * @return string A SQL condition, properly escaped, which evaluates to 'true' if the $level can be viewed with the current access level. + */ + public function getVisibilityConditionAbsolute($level) + { + if ($this->visibility_field != null) { + // The $sub.display_levels cell will contain allowed display levels + // for an access level of $this->visibility_field. + $sub = $this->addVisibilityFieldFilter($this->visibility_field); + return XDB::format('FIND_IN_SET({?}, ' . $sub . '.display_levels)', $level); + } else { + if ($this->visibility->isVisible($level)) { + return 'TRUE'; + } else { + return 'FALSE'; + } + } } 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) { @@ -1519,13 +185,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(); @@ -1538,75 +203,122 @@ class UserFilter extends PlFilter } } - private function getUIDList($uids = null, PlLimit &$limit) + 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)) { - $cond = ' AND a.uid IN ' . XDB::formatArray($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()'); + if (!empty($uids)) { + $cond = XDB::format(' AND a.uid IN {?}', $uids); + } + $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; } - private function getPIDList($pids = null, PlLimit &$limit) + private function getPIDList($pids = null, PlLimit $limit) { $this->requireProfiles(); $this->buildQuery(); $lim = $limit->getSql(); $cond = ''; if (!is_null($pids)) { - $cond = ' AND p.pid IN ' . XDB::formatArray($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()'); + $cond = XDB::format(' AND p.pid IN {?}', $pids); + } + $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; } + private static function defaultLimit($limit) { + if ($limit == null) { + return new PlLimit(); + } else { + return $limit; + } + } + /** Check that the user match the given rule. */ - public function checkUser(PlUser &$user) + public function checkUser(PlUser $user) { $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; } /** Check that the profile match the given rule. */ - public function checkProfile(Profile &$profile) + public function checkProfile(Profile $profile) { $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; } /** Default filter is on users */ - public function filter(array $users, PlLimit &$limit) + public function filter(array $users, $limit = null) { - return $this->filterUsers($users, $limit); + return $this->filterUsers($users, self::defaultLimit($limit)); } /** Filter a list of users to extract the users matching the rule. */ - public function filterUsers(array $users, PlLimit &$limit) + public function filterUsers(array $users, $limit = null) { + $limit = self::defaultLimit($limit); $this->requireAccounts(); $this->buildQuery(); $table = array(); @@ -1630,8 +342,9 @@ class UserFilter extends PlFilter /** Filter a list of profiles to extract the users matching the rule. */ - public function filterProfiles(array $profiles, PlLimit &$limit) + public function filterProfiles(array $profiles, $limit = null) { + $limit = self::defaultLimit($limit); $this->requireProfiles(); $this->buildQuery(); $table = array(); @@ -1653,87 +366,226 @@ class UserFilter extends PlFilter return $output; } - public function getUIDs(PlLimit &$limit) + public function getUIDs($limit = null) { + $limit = self::defaultLimit($limit); return $this->getUIDList(null, $limit); } - public function getPIDs(PlLimit &$limit) + public function getUID($pos = 0) + { + $uids =$this->getUIDList(null, new PlLimit(1, $pos)); + if (count($uids) == 0) { + return null; + } else { + return $uids[0]; + } + } + + public function getPIDs($limit = null) { + $limit = self::defaultLimit($limit); return $this->getPIDList(null, $limit); } - public function getUsers(PlLimit &$limit) + public function getPID($pos = 0) + { + $pids =$this->getPIDList(null, new PlLimit(1, $pos)); + if (count($pids) == 0) { + return null; + } else { + return $pids[0]; + } + } + + public function getUsers($limit = null) { return User::getBulkUsersWithUIDs($this->getUIDs($limit)); } - public function getProfiles(PlLimit &$limit) + public function getUser($pos = 0) + { + $uid = $this->getUID($pos); + if ($uid == null) { + return null; + } else { + return User::getWithUID($uid); + } + } + + public function iterUsers($limit = null) + { + return User::iterOverUIDs($this->getUIDs($limit)); + } + + public function getProfiles($limit = null, $fields = 0x0000, $visibility = null) + { + return Profile::getBulkProfilesWithPIDs($this->getPIDs($limit), $fields, $visibility); + } + + public function getProfile($pos = 0, $fields = 0x0000, $visibility = null) + { + $pid = $this->getPID($pos); + if ($pid == null) { + return null; + } else { + return Profile::get($pid, $fields, $visibility); + } + } + + public function iterProfiles($limit = null, $fields = 0x0000, $visibility = null) { - return Profile::getBulkProfilesWithPIDs($this->getPIDs($limit)); + return Profile::iterOverPIDs($this->getPIDs($limit), true, $fields, $visibility); } - public function get(PlLimit &$limit) + public function get($limit = null) { return $this->getUsers($limit); } + public function getIds($limit = null) + { + return $this->getUIDs(); + } + 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->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->lastcount; + return $this->lastprofilecount; } } - public function setCondition(PlFilterCondition &$cond) + public function setCondition(PlFilterCondition $cond) { $this->root =& $cond; $this->query = null; } - public function addSort(PlFilterOrder &$sort) + public function addSort(PlFilterOrder $sort) { + if (count($this->sort) == 0 && $sort instanceof PlFilterGroupableOrder) + { + $this->grouper = $sort; + } $this->sort[] = $sort; $this->orderby = null; } + public function export() + { + $export = array('conditions' => $this->root->export()); + if (!empty($this->sort)) { + $export['sorts'] = array(); + foreach ($this->sort as $sort) { + $export['sorts'][] = $sort->export(); + } + } + return $export; + } + + public function exportConditions() + { + return $this->root->export(); + } + + public static function fromExport(array $export) + { + $export = new PlDict($export); + if (!$export->has('conditions')) { + throw new Exception("Cannot build a user filter without conditions"); + } + $cond = UserFilterCondition::fromExport($export->v('conditions')); + $sorts = null; + if ($export->has('sorts')) { + $sorts = array(); + foreach ($export->v('sorts') as $sort) { + $sorts[] = UserFilterOrder::fromExport($sort); + } + } + return new UserFilter($cond, $sorts); + } + + public static function fromJSon($json) + { + $export = json_decode($json, true); + if (is_null($export)) { + throw new Exception("Invalid json: $json"); + } + return self::fromExport($json); + } + + public static function fromExportedConditions(array $export) + { + $cond = UserFilterCondition::fromExport($export); + return new UserFilter($cond); + } + + public static function fromJSonConditions($json) + { + $export = json_decode($json, true); + if (is_null($export)) { + throw new Exception("Invalid json: $json"); + } + return self::fromExportedConditions($json); + } + static public function getLegacy($promo_min, $promo_max) { if ($promo_min != 0) { $min = new UFC_Promo('>=', self::GRADE_ING, intval($promo_min)); } else { - $min = new UFC_True(); + $min = new PFC_True(); } if ($promo_max != 0) { $max = new UFC_Promo('<=', self::GRADE_ING, intval($promo_max)); } else { - $max = new UFC_True(); + $max = new PFC_True(); } return new UserFilter(new PFC_And($min, $max)); } static public function sortByName() { - return array(new UFO_Name(self::LASTNAME), new UFO_Name(self::FIRSTNAME)); + return array(new UFO_Name()); } static public function sortByPromo() { - return array(new UFO_Promo(), new UFO_Name(self::LASTNAME), new UFO_Name(self::FIRSTNAME)); + return array(new UFO_Promo(), new UFO_Name()); } static private function getDBSuffix($string) { - return preg_replace('/[^a-z0-9]/i', '', $string); + if (is_array($string)) { + if (count($string) == 1) { + return self::getDBSuffix(array_pop($string)); + } + return md5(implode('|', $string)); + } else { + return preg_replace('/[^a-z0-9]/i', '', $string); + } } @@ -1761,40 +613,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'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profiles', '$PID = sn.uid'); - if ($this->with_accounts) { - $joins['ap'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'account_profiles', '$ME.pid = $PID'); - $joins['a'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'accounts', '$UID = ap.uid'); - } - } else if ($this->with_profiles && $this->with_accounts) { - $joins['ap'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'account_profiles', '$ME.uid = $UID AND FIND_IN_SET(\'owner\', ap.perms)'); - $joins['p'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profiles', '$PID = ap.pid'); + 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'; @@ -1809,105 +676,58 @@ class UserFilter extends PlFilter protected function displayJoins() { if ($this->pd) { - return array('pd' => new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_display', '$ME.pid = $PID')); + return array('pd' => PlSqlJoin::left('profile_display', '$ME.pid = $PID')); } else { return array(); } } - /** NAMES + /** LOGGER */ - /* name tokens */ - const LASTNAME = 'lastname'; - const FIRSTNAME = 'firstname'; - const NICKNAME = 'nickname'; - const PSEUDONYM = 'pseudonym'; - const NAME = 'name'; - /* name variants */ - const VN_MARITAL = 'marital'; - const VN_ORDINARY = 'ordinary'; - const VN_OTHER = 'other'; - const VN_INI = 'ini'; - /* display names */ - const DN_FULL = 'directory_name'; - const DN_DISPLAY = 'yourself'; - const DN_YOURSELF = 'yourself'; - const DN_DIRECTORY = 'directory_name'; - const DN_PRIVATE = 'private_name'; - const DN_PUBLIC = 'public_name'; - const DN_SHORT = 'short_name'; - const DN_SORT = 'sort_name'; - - static public $name_variants = array( - self::LASTNAME => array(self::VN_MARITAL, self::VN_ORDINARY), - self::FIRSTNAME => array(self::VN_ORDINARY, self::VN_INI, self::VN_OTHER) - ); - - static public function assertName($name) - { - if (!Profile::getNameTypeId($name)) { - Platal::page()->kill('Invalid name type: ' . $name); - } - } - - static public function isDisplayName($name) - { - return $name == self::DN_FULL || $name == self::DN_DISPLAY - || $name == self::DN_YOURSELF || $name == self::DN_DIRECTORY - || $name == self::DN_PRIVATE || $name == self::DN_PUBLIC - || $name == self::DN_SHORT || $name == self::DN_SORT; - } - - private $pn = array(); - public function addNameFilter($type, $variant = null) - { - $this->requireProfiles(); - if (!is_null($variant)) { - $ft = $type . '_' . $variant; - } else { - $ft = $type; - } - $sub = '_' . $ft; - self::assertName($ft); - if (!is_null($variant) && $variant == 'other') { - $sub .= $this->option++; - } - $this->pn[$sub] = Profile::getNameTypeId($ft); - return $sub; + private $with_logger = false; + public function addLoggerFilter() + { + $this->with_logger = true; + $this->requireAccounts(); + return 'ls'; } - - protected function nameJoins() + protected function loggerJoins() { $joins = array(); - foreach ($this->pn as $sub => $type) { - $joins['pn' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_name', '$ME.pid = $PID AND $ME.typeid = ' . $type); + if ($this->with_logger) { + $joins['ls'] = PlSqlJoin::left('log_sessions', '$ME.uid = $UID'); } return $joins; } /** 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' => new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'search_name', '$ME.uid = $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 @@ -1924,25 +744,25 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->with_nat) { - $joins['ngc'] = new PlSqlJoin(PlSqlJoin::MODE_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'); + $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'); } return $joins; } /** EDUCATION */ - const GRADE_ING = 'Ing.'; - const GRADE_PHD = 'PhD'; - const GRADE_MST = 'M%'; + const GRADE_ING = Profile::DEGREE_X; + const GRADE_PHD = Profile::DEGREE_D; + const GRADE_MST = Profile::DEGREE_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) { if (!self::isGrade($grade)) { - Platal::page()->killError("Diplôme non valide"); + Platal::page()->killError("Diplôme non valide: $grade"); } } @@ -1975,17 +795,16 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->with_pee) { - $joins['pee'] = new PlSqlJoin(PlSqlJoin::MODE_INNER, 'profile_education_enum', 'pee.abbreviation = \'X\''); + $joins['pee'] = PlSqlJoin::inner('profile_education_enum', 'pee.abbreviation = \'X\''); } foreach ($this->pepe as $grade => $sub) { if ($this->isGrade($grade)) { - $joins['pe' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_education', '$ME.eduid = pee.id AND $ME.uid = $PID'); - $joins['pede' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_INNER, 'profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid AND $ME.abbreviation LIKE ' . - XDB::format('{?}', $grade)); + $joins['pe' . $sub] = PlSqlJoin::left('profile_education', '$ME.eduid = pee.id AND $ME.pid = $PID'); + $joins['pede' . $sub] = PlSqlJoin::inner('profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid AND $ME.degree LIKE {?}', $grade); } else { - $joins['pe' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_education', '$ME.uid = $PID'); - $joins['pee' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_INNER, 'profile_education_enum', '$ME.id = pe' . $sub . '.eduid'); - $joins['pede' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_INNER, 'profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid'); + $joins['pe' . $sub] = PlSqlJoin::left('profile_education', '$ME.pid = $PID'); + $joins['pee' . $sub] = PlSqlJoin::inner('profile_education_enum', '$ME.id = pe' . $sub . '.eduid'); + $joins['pede' . $sub] = PlSqlJoin::inner('profile_education_degree_enum', '$ME.id = pe' . $sub . '.degreeid'); } } return $joins; @@ -1999,7 +818,7 @@ class UserFilter extends PlFilter { $this->requireAccounts(); if (!is_null($group)) { - if (ctype_digit($group)) { + if (is_int($group) || ctype_digit($group)) { $index = $sub = $group; } else { $index = $group; @@ -2014,20 +833,52 @@ class UserFilter extends PlFilter return $sub; } + private $gpfm = array(); + public function addGroupFormerMemberFilter() + { + $this->requireAccounts(); + $sub = '_' . $this->option++; + $this->gpfm[] = $sub; + return $sub; + } + protected function groupJoins() { $joins = array(); foreach ($this->gpm as $sub => $key) { if (is_null($key)) { - $joins['gpa' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_INNER, 'groupex.asso'); - $joins['gpm' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'groupex.membres', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id'); - } else if (ctype_digit($key)) { - $joins['gpm' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'groupex.membres', '$ME.uid = $UID AND $ME.asso_id = ' . $key); + $joins['gpa' . $sub] = PlSqlJoin::inner('groups'); + $joins['gpm' . $sub] = PlSqlJoin::left('group_members', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id'); + } else if (is_int($key) || ctype_digit($key)) { + $joins['gpm' . $sub] = PlSqlJoin::left('group_members', '$ME.uid = $UID AND $ME.asso_id = ' . $key); } else { - $joins['gpa' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_INNER, 'groupex.asso', XDB::format('$ME.diminutif = {?}', $key)); - $joins['gpm' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'groupex.membres', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id'); + $joins['gpa' . $sub] = PlSqlJoin::inner('groups', '$ME.diminutif = {?}', $key); + $joins['gpm' . $sub] = PlSqlJoin::left('group_members', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id'); } } + foreach ($this->gpfm as $sub) { + $joins['gpfm' . $sub] = PlSqlJoin::left('group_former_members', '$ME.uid = $UID'); + } + return $joins; + } + + /** NLS + */ + private $nls = array(); + public function addNewsLetterFilter($nlid) + { + $this->requireAccounts(); + $sub = 'nl_' . $nlid; + $this->nls[$nlid] = $sub; + return $sub; + } + + protected function newsLetterJoins() + { + $joins = array(); + foreach ($this->nls as $key => $sub) { + $joins[$sub] = PlSqlJoin::left('newsletter_ins', '$ME.nlid = {?} AND $ME.uid = $UID', $key); + } return $joins; } @@ -2052,72 +903,88 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->with_bi) { - $joins['bi'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'binets_ins', '$ME.user_id = $PID'); + $joins['bi'] = PlSqlJoin::left('profile_binets', '$ME.pid = $PID'); } if ($this->with_bd) { - $joins['bd'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'binets_def', '$ME.id = bi.binet_id'); + $joins['bd'] = PlSqlJoin::left('profile_binet_enum', '$ME.id = bi.binet_id'); } return $joins; } /** EMAILS */ - private $e = array(); + private $ra = array(); + /** Allows filtering by redirection. + * @param $email If null, enable a left join on the email redirection table + * (email_redirect_account); otherwise, perform a left join on users having + * that email as a redirection. + * @return Suffix to use to access the adequate table. + */ public function addEmailRedirectFilter($email = null) { $this->requireAccounts(); - return $this->register_optional($this->e, $email); - } - - private $ve = array(); - public function addVirtualEmailFilter($email = null) + return $this->register_optional($this->ra, $email); + } + + const ALIAS_BEST = 'bestalias'; + const ALIAS_FORLIFE = 'forlife'; + const ALIAS_AUXILIARY = 'alias_aux'; + private $sa = array(); + /** Allows filtering by source email. + * @param $email If null, enable a left join on the email source table + * (email_source_account); otherwise, perform a left join on users having + * that email as a source email. + * @return Suffix to use to access the adequate table. + */ + public function addAliasFilter($email = null) { - $this->addAliasFilter(self::ALIAS_FORLIFE); - return $this->register_optional($this->ve, $email); + $this->requireAccounts(); + return $this->register_optional($this->sa, $email); } - const ALIAS_BEST = 'bestalias'; - const ALIAS_FORLIFE = 'forlife'; - private $al = array(); - public function addAliasFilter($alias = null) + private $with_rf = false; + /** Allows filtering by active redirection. + * @return Suffix to use to access the adequate table. + */ + public function addActiveEmailRedirectFilter($email = null) { $this->requireAccounts(); - return $this->register_optional($this->al, $alias); + $this->with_rf = true; } protected function emailJoins() { global $globals; $joins = array(); - foreach ($this->e as $sub=>$key) { - if (is_null($key)) { - $joins['e' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'emails', '$ME.uid = $UID AND $ME.flags != \'filter\''); + foreach ($this->ra as $sub => $redirections) { + if (is_null($redirections)) { + $joins['ra' . $sub] = PlSqlJoin::left('email_redirect_account', '$ME.uid = $UID AND $ME.type != \'imap\''); } else { - $joins['e' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'emails', XDB::format('$ME.uid = $UID AND $ME.flags != \'filter\' AND $ME.email = {?}', $key)); + if (!is_array($redirections)) { + $key = array($redirections); + } + $joins['ra' . $sub] = PlSqlJoin::left('email_redirect_account', '$ME.uid = $UID AND $ME.type != \'imap\' + AND $ME.redirect IN {?}', $redirections); } } - foreach ($this->al as $sub=>$key) { - if (is_null($key)) { - $joins['al' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'aliases', '$ME.id = $UID AND $ME.type IN (\'alias\', \'a_vie\')'); - } else if ($key == self::ALIAS_BEST) { - $joins['al' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'aliases', '$ME.id = $UID AND $ME.type IN (\'alias\', \'a_vie\') AND FIND_IN_SET(\'bestalias\', $ME.flags)'); - } else if ($key == self::ALIAS_FORLIFE) { - $joins['al' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'aliases', '$ME.id = $UID AND $ME.type = \'a_vie\''); + foreach ($this->sa as $sub => $emails) { + if (is_null($emails)) { + $joins['sa' . $sub] = PlSqlJoin::left('email_source_account', '$ME.uid = $UID'); + } else if ($sub == self::ALIAS_BEST) { + $joins['sa' . $sub] = PlSqlJoin::left('email_source_account', '$ME.uid = $UID AND FIND_IN_SET(\'bestalias\', $ME.flags)'); + } else if ($sub == self::ALIAS_FORLIFE) { + $joins['sa' . $sub] = PlSqlJoin::left('email_source_account', '$ME.uid = $UID AND $ME.type = \'forlife\''); + } else if ($sub == self::ALIAS_AUXILIARY) { + $joins['sa' . $sub] = PlSqlJoin::left('email_source_account', '$ME.uid = $UID AND $ME.type = \'alias_aux\''); } else { - $joins['al' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'aliases', XDB::format('$ME.id = $UID AND $ME.type IN (\'alias\', \'a_vie\') AND $ME.alias = {?}', $key)); + if (!is_array($emails)) { + $key = array($emails); + } + $joins['sa' . $sub] = PlSqlJoin::left('email_source_account', '$ME.uid = $UID AND $ME.email IN {?}', $emails); } } - foreach ($this->ve as $sub=>$key) { - if (is_null($key)) { - $joins['v' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'virtual', '$ME.type = \'user\''); - } else { - $joins['v' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'virtual', XDB::format('$ME.type = \'user\' AND $ME.alias = {?}', $key)); - } - $joins['vr' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'virtual_redirect', XDB::format('$ME.vid = v' . $sub . '.vid - AND ($ME.redirect IN (CONCAT(al_forlife.alias, \'@\', {?}), - CONCAT(al_forlife.alias, \'@\', {?}), - a.email))', - $globals->mail->domain, $globals->mail->domain2)); + if ($this->with_rf) { + $joins['rf'] = PlSqlJoin::left('email_redirect_account', '$ME.uid = $UID AND $ME.type != \'imap\' AND $ME.flags = \'active\'');; } return $joins; } @@ -2125,44 +992,28 @@ class UserFilter extends PlFilter /** ADDRESSES */ - private $with_pa = false; - public function addAddressFilter() + private $types = array(); + public function addAddressFilter($type) { $this->requireProfiles(); $this->with_pa = true; - return 'pa'; - } - - private $with_pac = false; - public function addAddressCountryFilter() - { - $this->requireProfiles(); - $this->addAddressFilter(); - $this->with_pac = true; - return 'gc'; - } - private $with_pal = false; - public function addAddressLocalityFilter() - { - $this->requireProfiles(); - $this->addAddressFilter(); - $this->with_pal = true; - return 'gl'; + $sub = '_' . $this->option++; + $this->types[$type] = $sub; + return $sub; } protected function addressJoins() { $joins = array(); - if ($this->with_pa) { - $joins['pa'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_addresses', '$ME.pid = $PID'); - } - if ($this->with_pac) { - $joins['gc'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'geoloc_countries', '$ME.iso_3166_1_a2 = pa.countryID'); - } - if ($this->with_pal) { - $joins['gl'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'geoloc_localities', '$ME.id = pa.localityID'); + foreach ($this->types as $type => $sub) { + $joins['pa' . $sub] = PlSqlJoin::inner('profile_addresses', '$ME.pid = $PID'); + $joins['pac' . $sub] = PlSqlJoin::inner('profile_addresses_components', + '$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'); + $joins['pace' . $sub] = PlSqlJoin::inner('profile_addresses_components_enum', + '$ME.id = pac' . $sub . '.component_id AND FIND_IN_SET({?}, $ME.types)', $type); } + return $joins; } @@ -2178,10 +1029,10 @@ class UserFilter extends PlFilter $this->requireProfiles(); $this->pc = true; if ($type == UFC_Corps::CURRENT) { - $pce['pcec'] = 'current_corpsid'; + $this->pce['pcec'] = 'current_corpsid'; return 'pcec'; } else if ($type == UFC_Corps::ORIGIN) { - $pce['pceo'] = 'original_corpsid'; + $this->pce['pceo'] = 'original_corpsid'; return 'pceo'; } } @@ -2198,13 +1049,13 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->pc) { - $joins['pc'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_corps', '$ME.uid = $UID'); + $joins['pc'] = PlSqlJoin::left('profile_corps', '$ME.pid = $PID'); } if ($this->pcr) { - $joins['pcr'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_corps_rank_enum', '$ME.id = pc.rankid'); + $joins['pcr'] = PlSqlJoin::left('profile_corps_rank_enum', '$ME.id = pc.rankid'); } foreach($this->pce as $sub => $field) { - $joins[$sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_corps_enum', '$ME.id = pc.' . $field); + $joins[$sub] = PlSqlJoin::left('profile_corps_enum', '$ME.id = pc.' . $field); } return $joins; } @@ -2212,30 +1063,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() { @@ -2251,44 +1090,35 @@ 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() { $joins = array(); if ($this->with_pj) { - $joins['pj'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_job', '$ME.uid = $UID'); + $joins['pj'] = PlSqlJoin::left('profile_job', '$ME.pid = $PID'); } if ($this->with_pje) { - $joins['pje'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_job_enum', '$ME.id = pj.jobid'); - } - if ($this->with_pjse) { - $joins['pjse'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_job_sector_enum', '$ME.id = pj.sectorid'); - } - if ($this->with_pjsse) { - $joins['pjsse'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_job_subsector_enum', '$ME.id = pj.subsectorid'); + $joins['pje'] = PlSqlJoin::left('profile_job_enum', '$ME.id = pj.jobid'); } - if ($this->with_pjssse) { - $joins['pjssse'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_job_subsubsector_enum', '$ME.id = pj.subsubsectorid'); - } - if ($this->with_pja) { - $joins['pja'] = new PlSqlJoin(PlSqlJoin::MODE_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; } @@ -2308,7 +1138,7 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->with_pnw) { - $joins['pnw'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_networking', '$ME.uid = $UID'); + $joins['pnw'] = PlSqlJoin::left('profile_networking', '$ME.pid = $PID'); } return $joins; } @@ -2329,7 +1159,7 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->with_ptel) { - $joins['ptel'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_phones', '$ME.uid = $UID'); + $joins['ptel'] = PlSqlJoin::left('profile_phones', '$ME.pid = $PID'); } return $joins; } @@ -2349,7 +1179,38 @@ class UserFilter extends PlFilter { $joins = array(); if ($this->with_pmed) { - $joins['pmed'] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'profile_medals_sub', '$ME.uid = $UID'); + $joins['pmed'] = PlSqlJoin::left('profile_medals', '$ME.pid = $PID'); + } + return $joins; + } + + /** DELTATEN + */ + private $dts = array(); + const DELTATEN = 1; + const DELTATEN_MESSAGE = 2; + // TODO: terms + + public function addDeltaTenFilter($type) + { + $this->requireProfiles(); + switch ($type) { + case self::DELTATEN: + $this->dts['pdt'] = 'profile_deltaten'; + return 'pdt'; + case self::DELTATEN_MESSAGE: + $this->dts['pdtm'] = 'profile_deltaten'; + return 'pdtm'; + default: + Platal::page()->killError("Undefined DeltaTen filter."); + } + } + + protected function deltatenJoins() + { + $joins = array(); + foreach ($this->dts as $sub => $tab) { + $joins[$sub] = PlSqlJoin::left($tab, '$ME.pid = $PID'); } return $joins; } @@ -2358,23 +1219,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(); + $this->requireProfiles(); 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."); } @@ -2384,7 +1251,10 @@ class UserFilter extends PlFilter { $joins = array(); foreach ($this->pms as $sub => $tab) { - $joins[$sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, $tab, '$ME.uid = $UID'); + $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; } @@ -2394,7 +1264,7 @@ class UserFilter extends PlFilter private $cts = array(); public function addContactFilter($uid = null) { - $this->requireAccounts(); + $this->requireProfiles(); return $this->register_optional($this->cts, is_null($uid) ? null : 'user_' . $uid); } @@ -2403,9 +1273,9 @@ class UserFilter extends PlFilter $joins = array(); foreach ($this->cts as $sub=>$key) { if (is_null($key)) { - $joins['c' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'contacts', '$ME.contact = $UID'); + $joins['c' . $sub] = PlSqlJoin::left('contacts', '$ME.contact = $PID'); } else { - $joins['c' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'contacts', XDB::format('$ME.uid = {?} AND $ME.contact = $UID', substr($key, 5))); + $joins['c' . $sub] = PlSqlJoin::left('contacts', '$ME.uid = {?} AND $ME.contact = $PID', substr($key, 5)); } } return $joins; @@ -2440,46 +1310,175 @@ class UserFilter extends PlFilter $joins = array(); foreach ($this->w as $sub=>$key) { if (is_null($key)) { - $joins['w' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch'); + $joins['w' . $sub] = PlSqlJoin::left('watch'); } else { - $joins['w' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch', XDB::format('$ME.uid = {?}', substr($key, 5))); + $joins['w' . $sub] = PlSqlJoin::left('watch', '$ME.uid = {?}', substr($key, 5)); } } foreach ($this->wn as $sub=>$key) { if (is_null($key)) { - $joins['wn' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch_nonins', '$ME.ni_id = $UID'); + $joins['wn' . $sub] = PlSqlJoin::left('watch_nonins', '$ME.ni_id = $UID'); } else { - $joins['wn' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch_nonins', XDB::format('$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5))); + $joins['wn' . $sub] = PlSqlJoin::left('watch_nonins', '$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5)); } } foreach ($this->wn as $sub=>$key) { if (is_null($key)) { - $joins['wn' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch_nonins', '$ME.ni_id = $UID'); + $joins['wn' . $sub] = PlSqlJoin::left('watch_nonins', '$ME.ni_id = $UID'); } else { - $joins['wn' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch_nonins', XDB::format('$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5))); + $joins['wn' . $sub] = PlSqlJoin::left('watch_nonins', '$ME.uid = {?} AND $ME.ni_id = $UID', substr($key, 5)); } } foreach ($this->wp as $sub=>$key) { if (is_null($key)) { - $joins['wp' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch_promo'); + $joins['wp' . $sub] = PlSqlJoin::left('watch_promo'); } else { - $joins['wp' . $sub] = new PlSqlJoin(PlSqlJoin::MODE_LEFT, 'watch_promo', XDB::format('$ME.uid = {?}', substr($key, 5))); + $joins['wp' . $sub] = PlSqlJoin::left('watch_promo', '$ME.uid = {?}', substr($key, 5)); } } return $joins; } + + + /** 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; + public function addMarketingHash() + { + $this->requireAccounts(); + $this->with_rm = true; + } + + protected function marketingJoins() + { + if ($this->with_rm) { + return array('rm' => PlSqlJoin::left('register_marketing', '$ME.uid = $UID')); + } else { + return array(); + } + } + + + /** PARTNER SHARING + */ + + // Lists partner shortnames in use, as a $partner_shortname => true map. + private $ppss = array(); + + /** Add a filter on user having settings for a given partner. + * @param $partner_id the ID of the partner + * @return the name of the table to use in joins (e.g ppss_$partner_id). + */ + public function addPartnerSharingFilter($partner_id) + { + $this->requireProfiles(); + $sub = "ppss_" . $partner_id; + $this->ppss[$sub] = $partner_id; + return $sub; + } + + protected function partnerSharingJoins() + { + $joins = array(); + foreach ($this->ppss as $sub => $partner_id) { + $joins[$sub] = PlSqlJoin::left('profile_partnersharing_settings', '$ME.pid = $PID AND $ME.partner_id = {?} AND $ME.sharing_level != \'none\'', $partner_id); + } + return $joins; + } + + public function restrictVisibilityForPartner($partner_id) + { + $sub = $this->addPartnerSharingFilter($partner_id); + $this->visibility_field = $sub . '.sharing_level'; + } + + /** VISIBILITY + */ + private $vlevels = array(); + private $vfields = array(); + public function addVisibilityAbsoluteFilter($level) + { + $sub = 'pvel_' . $level; + $this->vlevels[$level] = $sub; + return $sub; + } + + public function addVisibilityFieldFilter($field) + { + $sub = 'pvef_' . self::getDBSuffix($field); + $this->vfields[$field] = $sub; + return $sub; + } + + /** Since this method might perform inner joins on tables which have been + * joined previously (e.g when using addVisibilityFieldFilter), it has to + * come after the Joins() methods for those tables. + * This is due to the implementation logic for discovering joins and the + * ordering used by PHP introspection. + */ + protected function visibilityJoins() + { + $joins = array(); + foreach ($this->vlevels as $level => $sub) { + $joins[$sub] = PlSqlJoin::inner('profile_visibility_enum', '$ME.access_level = {?}', $level); + } + foreach ($this->vfields as $field => $sub) { + $joins[$sub] = PlSqlJoin::inner('profile_visibility_enum', '$ME.access_level = ' . $field); + } + return $joins; + } + } // }}} - // {{{ class ProfileFilter class ProfileFilter extends UserFilter { - public function get(PlLimit &$limit) + public function get($limit = null) { return $this->getProfiles($limit); } + + public function getIds($limit = null) + { + return $this->getPIDs(); + } + + 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(); + } } // }}} -// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: +// vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8: ?>