+ $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 implements 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 implements 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 implements 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 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.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 implements 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 implements 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 implements 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 implements 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 . ')';
+ }
+ }
+
+ private function formatJoin(array $joins)
+ {
+ $str = '';
+ foreach ($joins as $key => $infos) {
+ $mode = $infos[0];
+ $table = $infos[1];
+ if ($mode == 'inner') {
+ $str .= 'INNER JOIN ';
+ } else if ($mode == 'left') {
+ $str .= 'LEFT JOIN ';
+ } else {
+ Platal::page()->kill("Join mode error");
+ }
+ $str .= $table . ' AS ' . $key;
+ if (isset($infos[2])) {
+ $str .= ' ON (' . str_replace(array('$ME', '$PID', '$UID'), array($key, 'p.pid', 'a.uid'), $infos[2]) . ')';
+ }
+ $str .= "\n";
+ }
+ return $str;
+ }
+
+ private function buildJoins()
+ {
+ $joins = array();
+ foreach (self::$joinMethods as $method) {
+ $joins = array_merge($joins, $this->$method());
+ }
+ return $this->formatJoin($joins);
+ }
+
+ private function getUIDList($uids = null, $count = null, $offset = null)
+ {
+ $this->buildQuery();
+ $limit = '';
+ if (!is_null($count)) {
+ if (!is_null($offset)) {
+ $limit = XDB::format('LIMIT {?}, {?}', (int)$offset, (int)$count);
+ } else {
+ $limit = XDB::format('LIMIT {?}', (int)$count);
+ }
+ }
+ $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 . '
+ ' . $limit);
+ $this->lastcount = (int)XDB::fetchOneCell('SELECT FOUND_ROWS()');
+ return $fetched;
+ }
+
+ /** Check that the user match the given rule.
+ */
+ public function checkUser(PlUser &$user)
+ {
+ $this->buildQuery();
+ $count = (int)XDB::fetchOneCell('SELECT COUNT(*)
+ ' . $this->query . XDB::format(' AND a.uid = {?}', $user->id()));
+ return $count == 1;
+ }
+
+ /** Filter a list of user to extract the users matching the rule.
+ */
+ public function filter(array $users, $count = null, $offset = null)
+ {
+ $this->buildQuery();
+ $table = array();
+ $uids = array();
+ foreach ($users as $user) {
+ if ($user instanceof PlUser) {
+ $uid = $user->id();
+ } else {
+ $uid = $user;
+ }
+ $uids[] = $uid;
+ $table[$uid] = $user;
+ }
+ $fetched = $this->getUIDList($uids, $count, $offset);
+ $output = array();
+ foreach ($fetched as $uid) {
+ $output[] = $table[$uid];
+ }
+ return $output;
+ }
+
+ public function getUIDs($count = null, $offset = null)
+ {
+ return $this->getUIDList(null, $count, $offset);
+ }
+
+ public function getUsers($count = null, $offset = null)
+ {
+ return User::getBulkUsersWithUIDs($this->getUIDs($count, $offset));
+ }
+
+ public function getTotalCount()
+ {
+ if (is_null($this->lastcount)) {
+ $this->buildQuery();
+ return (int)XDB::fetchOneCell('SELECT COUNT(DISTINCT a.uid)
+ ' . $this->query);
+ } else {
+ return $this->lastcount;
+ }
+ }
+
+ public function setCondition(UserFilterCondition &$cond)
+ {
+ $this->root =& $cond;
+ $this->query = null;
+ }
+
+ public function addSort(UserFilterOrder &$sort)
+ {
+ $this->sort[] = $sort;
+ $this->orderby = null;
+ }
+
+ 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();
+ }
+ if ($promo_max != 0) {
+ $max = new UFC_Promo('<=', self::GRADE_ING, intval($promo_max));
+ } else {
+ $max = new UFC_True();
+ }
+ return new UserFilter(new UFC_And($min, $max));
+ }
+
+ static public function sortByName()
+ {
+ return array(new UFO_Name(self::LASTNAME), new UFO_Name(self::FIRSTNAME));
+ }
+
+ static public function sortByPromo()
+ {
+ return array(new UFO_Promo(), new UFO_Name(self::LASTNAME), new UFO_Name(self::FIRSTNAME));
+ }
+
+ static private function getDBSuffix($string)
+ {
+ return preg_replace('/[^a-z0-9]/i', '', $string);
+ }
+
+
+ /** Stores a new (and unique) table alias in the &$table table
+ * @param &$table Array in which the table alias must be stored
+ * @param $val Value which will then be used to build the join
+ * @return Name of the newly created alias
+ */
+ private $option = 0;
+ private function register_optional(array &$table, $val)
+ {
+ if (is_null($val)) {
+ $sub = $this->option++;
+ $index = null;
+ } else {
+ $sub = self::getDBSuffix($val);
+ $index = $val;
+ }
+ $sub = '_' . $sub;
+ $table[$sub] = $index;
+ return $sub;
+ }
+
+ /** DISPLAY
+ */
+ const DISPLAY = 'display';
+ private $pd = false;
+ public function addDisplayFilter()
+ {
+ $this->pd = true;
+ return '';
+ }
+
+ private function displayJoins()
+ {
+ if ($this->pd) {
+ return array('pd' => array('left', 'profile_display', '$ME.pid = $PID'));
+ } else {
+ return array();
+ }
+ }
+
+ /** NAMES
+ */
+ /* 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');
+ }
+ }
+
+ 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)
+ {
+ 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 function nameJoins()
+ {
+ $joins = array();
+ foreach ($this->pn as $sub => $type) {
+ $joins['pn' . $sub] = array('left', 'profile_name', '$ME.pid = $PID AND $ME.typeid = ' . $type);
+ }
+ return $joins;
+ }
+
+
+ /** EDUCATION
+ */
+ const GRADE_ING = 'Ing.';
+ const GRADE_PHD = 'PhD';
+ const GRADE_MST = 'M%';
+ static public function isGrade($grade)
+ {
+ return $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");
+ }
+ }
+
+ static public function promoYear($grade)
+ {
+ // XXX: Definition of promotion for phds and masters might change in near future.
+ return ($grade == UserFilter::GRADE_ING) ? 'entry_year' : 'grad_year';
+ }
+
+ private $pepe = array();
+ private $with_pee = false;
+ public function addEducationFilter($x = false, $grade = null)
+ {
+ if (!$x) {
+ $index = $this->option;
+ $sub = $this->option++;
+ } else {
+ self::assertGrade($grade);
+ $index = $grade;
+ $sub = $grade[0];
+ $this->with_pee = true;
+ }
+ $sub = '_' . $sub;
+ $this->pepe[$index] = $sub;
+ return $sub;
+ }
+
+ private function educationJoins()
+ {
+ $joins = array();
+ if ($this->with_pee) {
+ $joins['pee'] = array('inner', 'profile_education_enum', 'pee.abbreviation = \'X\'');