-// {{{ class UFC_Address
-/** Filters users based on their address
- * @param $field Field of the address used for filtering (city, street, ...)
- * @param $text Text for filter
- * @param $mode Mode for search (PREFIX, SUFFIX, ...)
- */
-class UFC_Address implements UserFilterCondition
-{
- const PREFIX = 1;
- const SUFFIX = 2;
- const CONTAINS = 3;
-
- private $field;
- private $text;
- private $mode;
-
- public function __construct($field, $text, $mode)
- {
- $this->field = $field;
- $this->text = $text;
- $this->mode = $mode;
- }
-
- public function buildCondition(UserFilter &$uf)
- {
- $left = 'pa.' . $field;
- $op = ' LIKE ';
- 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;
- $uf->addAddressFilter();
- return $cond;
- }
-}
-// }}}
-
-// {{{ 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(UserFilter &$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(UserFilter &$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 extends 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(UserFilter &$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 $sector The sector searched
- * @param $subsector The subsector
- * @param $subsubsector The subsubsector
- */
-class UFC_Job_Sectorization extends UserFilterCondition
-{
-
- private $sector;
- private $subsector;
- private $subsubsector;
-
- public function __construct($sector = null, $subsector = null, $subsubsector = null)
- {
- $this->sector = $sector;
- $this->subsector = $subsector;
- $this->subsubsector = $subsubsector;
- }
-
- public function buildCondition(UserFilter &$uf)
- {
- // No need to add the JobFilter, it will be done by addJobSectorizationFilter
- $conds = array();
- if ($this->sector !== null) {
- $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SECTOR);
- $conds[] = $sub . '.id = ' . XDB::format('{?}', $this->sector);
- }
- if ($this->subsector !== null) {
- $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SUBSECTOR);
- $conds[] = $sub . '.id = ' . XDB::format('{?}', $this->subsector);
- }
- if ($this->subsubsector !== null) {
- $sub = $uf->addJobSectorizationFilter(UserFilter::JOB_SUBSUBSECTOR);
- $conds[] = $sub . '.id = ' . XDB::format('{?}', $this->subsubsector);
- }
- return implode(' AND ', $conds);
- }
-}
-// }}}
-
-// {{{ 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 extends UserFilterCondition
-{
-
- /** Meta-filters
- * Built with binary OR on UserFilter::JOB_*
- */
- const ANY = 31;
- const SECTORIZATION = 15;
-
- private $description;
- private $fields;
-
- public function __construct($description)
- {
- $this->fields = $fields;
- $this->description = $description;
- }
-
- public function buildCondition(UserFilter &$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_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 extends UserFilterCondition
-{
- private $type;
- private $value;
-
- public function __construct($type, $value)
- {
- $this->type = $type;
- $this->value = $value;
- }
-
- public function buildCondition(UserFilter &$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 extends 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.inc.php');
- $this->number = $number;
- $this->num_type = $num_type;
- $this->phone_type = format_phone_number($phone_type);
- }
-
- public function buildCondition(UserFilter &$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 extends UserFilterCondition
-{
- private $medal;
- private $grade;
-
- public function __construct($medal, $grade = null)
- {
- $this->medal = $medal;
- $this->grade = $grade;
- }
-
- public function buildCondition(UserFilter &$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 extends UserFilterCondition
-{
- private $expertise;
-
- public function __construct($expertise)
- {
- $this->expertise = $expertise;
- }
-
- public function buildCondition(UserFilter &$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 extends UserFilterCondition
-{
- private $country;
-
- public function __construct($country)
- {
- $this->country = $country;
- }
-
- public function buildCondition(UserFilter &$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 sector
- * @param $subsector Subsector (null for any)
- */
-class UFC_Mentor_Sectorization extends UserFilterCondition
-{
- private $sector;
- private $subsector;
-
- public function __construct($sector, $subsector = null)
- {
- $this->sector = $sector;
- $this->subsubsector = $subsector;
- }
-
- public function buildCondition(UserFilter &$uf)
- {
- $sub = $uf->addMentorFilter(UserFilter::MENTOR_SECTOR);
- $conds = array();
- $conds[] = $sub . '.sectorid = ' . XDB::format('{?}', $this->sector);
- if ($this->subsector != null) {
- $conds[] = $sub . '.subsectorid = ' . XDB::format('{?}', $this->subsector);
- }
- return implode(' AND ', $conds);
- }
-}
-// }}}
-
-// {{{ 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(UserFilter &$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(UserFilter &$uf)
- {
- if (!$this->user->watch('registration')) {
- return UserFilterCondition::COND_FALSE;
- }
- $uids = $this->user->watchUsers();
- if (count($uids) == 0) {
- return UserFilterCondition::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(UserFilter &$uf)
- {
- $promos = $this->user->watchPromos();
- if (count($promos) == 0) {
- return UserFilterCondition::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(UserFilter &$uf)
- {
- if (!$this->user->watchContacts()) {
- return UserFilterCondition::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
-{
- protected $desc = false;
- public function __construct($desc = false)
- {
- $this->desc = $desc;
- }
-
- public function buildSort(UserFilter &$uf)
- {
- $sel = $this->getSortTokens($uf);
- if (!is_array($sel)) {
- $sel = array($sel);
- }
- if ($this->desc) {
- foreach ($sel as $k=>$s) {
- $sel[$k] = $s . ' DESC';
- }
- }
- return $sel;
- }
-
- /** 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(UserFilter &$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(UserFilter &$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_Registration
-/** Sorts users based on registration date
- */
-class UFO_Registration extends UserFilterOrder
-{
- protected function getSortTokens(UserFilter &$uf)
- {
- return 'a.registration_date';
- }
-}
-// }}}
-
-// {{{ class UFO_Birthday
-/** Sorts users based on next birthday date
- */
-class UFO_Birthday extends UserFilterOrder
-{
- protected function getSortTokens(UserFilter &$uf)
- {
- return 'p.next_birthday';
- }
-}
-// }}}
-
-// {{{ class UFO_ProfileUpdate
-/** Sorts users based on last profile update
- */
-class UFO_ProfileUpdate extends UserFilterOrder
-{
- protected function getSortTokens(UserFilter &$uf)
- {
- return 'p.last_change';
- }
-}
-// }}}
-
-// {{{ class UFO_Death
-/** Sorts users based on death date
- */
-class UFO_Death extends UserFilterOrder
-{
- protected function getSortTokens(UserFilter &$uf)
- {
- return 'p.deathdate';
- }
-}
-// }}}
-
-
-/***********************************
- *********************************
- USER FILTER CLASS
- *********************************
- ***********************************/
-
-// {{{ class UserFilter
-/** This class provides a convenient and centralized way of filtering users.
- *
- * Usage:
- * $uf = new UserFilter(new UFC_Blah($x, $y), new UFO_Coin($z, $t));
- *
- * Resulting UserFilter can be used to:
- * - get a list of User objects matching the filter
- * - get a list of UIDs matching the filter
- * - get the number of users matching the filter
- * - check whether a given User matches the filter
- * - filter a list of User objects depending on whether they match the filter
- *
- * Usage for UFC and UFO objects:
- * A UserFilter will call all private functions named XXXJoins.
- * These functions must return an array containing the list of join
- * required by the various UFC and UFO associated to the UserFilter.
- * Entries in those returned array are of the following form:
- * 'join_tablealias' => array('join_type', 'joined_table', 'join_criter')
- * which will be translated into :
- * join_type JOIN joined_table AS join_tablealias ON (join_criter)
- * 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.
- *
- * 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
- * the required joins ; such a function shall return the "join_tablealias" to use
- * 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
- * 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:
- * 'pj' => array('left', 'profile_job', '$ME.pid = $UID');
- *
- * The 'register_optional' function can be used to generate unique table aliases when
- * the same table has to be joined several times with different aliases.
- */
-class UserFilter
-{
- static private $joinMethods = array();
-
- private $root;
- private $sort = array();
- private $query = null;
- private $orderby = null;
-
- private $lastcount = null;
-
- public function __construct($cond = null, $sort = null)
- {
- if (empty(self::$joinMethods)) {
- $class = new ReflectionClass('UserFilter');
- foreach ($class->getMethods() as $method) {
- $name = $method->getName();
- if (substr($name, -5) == 'Joins' && $name != 'buildJoins') {
- self::$joinMethods[] = $name;
- }
- }
- }
- if (!is_null($cond)) {
- if ($cond instanceof UserFilterCondition) {
- $this->setCondition($cond);
- }
- }
- if (!is_null($sort)) {
- if ($sort instanceof UserFilterOrder) {
- $this->addSort($sort);
- } else if (is_array($sort)) {
- foreach ($sort as $s) {
- $this->addSort($s);
- }
- }
- }
- }
-
- private function buildQuery()
- {
- if (is_null($this->orderby)) {
- $orders = array();
- foreach ($this->sort as $sort) {
- $orders = array_merge($orders, $sort->buildSort($this));
- }
- if (count($orders) == 0) {
- $this->orderby = '';
- } else {
- $this->orderby = 'ORDER BY ' . implode(', ', $orders);
- }
- }
- if (is_null($this->query)) {
- $where = $this->root->buildCondition($this);
- $joins = $this->buildJoins();
- $this->query = 'FROM accounts AS a
- LEFT JOIN account_profiles AS ap ON (ap.uid = a.uid AND FIND_IN_SET(\'owner\', ap.perms))
- LEFT JOIN profiles AS p ON (p.pid = ap.pid)
- ' . $joins . '
- WHERE (' . $where . ')';
- }