Fix 'reversed' field handling for AX newsletter.
[platal.git] / include / ufbuilder.inc.php
index 45c589b..18d4195 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2010 Polytechnique.org                              *
+ *  Copyright (C) 2003-2014 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
  ***************************************************************************/
 
+// {{{ class StoredUserFilterBuilder
+class StoredUserFilterBuilder
+{
+    // Possible stored types (currently only 'ufb' exists)
+    const TYPE_UFB = 'ufb';
+
+    protected $ufb;
+    protected $env;
+    protected $ufc;
+
+    public function __construct(UserFilterBuilder $ufb, PlFilterCondition $ufc = null, array $env = array())
+    {
+        $this->ufb = $ufb;
+        $this->ufc = $ufc;
+        $this->env = $env;
+    }
+
+    public function export()
+    {
+        $export = new PlDict();
+        $export->set('type', self::TYPE_UFB);
+        $export->set('condition', $this->ufc->export());
+        $export->set('env', $this->env);
+        return $export;
+    }
+
+    public function getEnv()
+    {
+        return $this->env;
+    }
+
+    public function fillFromExport($export)
+    {
+        $export = new PlDict($export);
+        if (!$export->has('type')) {
+            throw new Exception("Missing 'type' field in export.");
+        }
+        if ($export->s('type') != self::TYPE_UFB) {
+            throw new Exception("Unknown type '$type' in export.");
+        }
+        $this->ufc = UserFilterCondition::fromExport($export->v('condition'));
+        $this->env = $export->v('env', array());
+    }
+
+    public function updateFromEnv($env)
+    {
+        $this->ufb->setFakeEnv($env);
+        if ($this->ufb->isValid()) {
+            $this->env = $env;
+            $this->ufc = $this->ufb->getUFC();
+            return true;
+        } else {
+            $this->ufb->clearFakeEnv();
+            return false;
+        }
+    }
+
+    public function refresh()
+    {
+        if ($this->isValid()) {
+            $this->ufc = $this->ufb->getUFC();
+        }
+    }
+
+    public function getUFC()
+    {
+        return $this->ufc;
+    }
+
+    public function isValid()
+    {
+        $this->ufb->setFakeEnv($this->env);
+        return $this->ufb->isValid();
+    }
+
+    public function isEmpty()
+    {
+        $this->ufb->setFakeEnv($this->env);
+        return $this->ufb->isEmpty();
+    }
+}
+// }}}
+
 // {{{ class UserFilterBuilder
 class UserFilterBuilder
 {
@@ -27,6 +110,7 @@ class UserFilterBuilder
     private $valid = true;
     private $ufc = null;
     private $orders = array();
+    private $fake_env = null;
 
     /** Constructor
      * @param $fields An array of UFB_Field objects
@@ -38,6 +122,16 @@ class UserFilterBuilder
         $this->envprefix   = $envprefix;
     }
 
+    public function setFakeEnv($env)
+    {
+        $this->fake_env = new PlDict($env);
+    }
+
+    public function clearFakeEnv()
+    {
+        $this->fake_env = null;
+    }
+
     /** Builds the UFC; returns as soon as a field says it is invalid
      */
     private function buildUFC()
@@ -48,19 +142,19 @@ class UserFilterBuilder
         $this->ufc = new PFC_And();
 
         foreach ($this->fields as $field) {
-            $this->valid = $field->apply(&$this);
+            $this->valid = $field->apply($this);
             if (!$this->valid) {
                 return;
             }
         }
     }
 
-    public function addCond(PlFilterCondition &$cond)
+    public function addCond(PlFilterCondition $cond)
     {
         $this->ufc->addChild($cond);
     }
 
-    public function addOrder(PlFilterOrder &$order)
+    public function addOrder(PlFilterOrder $order)
     {
         $this->order[] = $order;
     }
@@ -89,7 +183,11 @@ class UserFilterBuilder
     {
         $this->buildUFC();
         if ($this->valid) {
-            return $this->ufc;
+            if ($this->isEmpty()) {
+                return new PFC_True();
+            } else {
+                return $this->ufc;
+            }
         } else {
             return new PFC_False();
         }
@@ -103,41 +201,98 @@ class UserFilterBuilder
         return $this->orders;
     }
 
+    public function getEnvFieldNames()
+    {
+        $fields = array();
+        foreach ($this->fields as $ufbf) {
+            $fields = array_merge($fields, $ufbf->getEnvFieldNames());
+        }
+        return array_unique($fields);
+    }
+
+    public function getEnv()
+    {
+        $values = array();
+        foreach ($this->getEnvFieldNames() as $field) {
+            if ($this->has($field)) {
+                $values[$field] = $this->v($field);
+            }
+        }
+        return $values;
+    }
+
+    public function setEnv($values)
+    {
+        foreach ($this->getEnvFieldNames() as $field) {
+            if (array_key_exists($field, $values)) {
+                Env::set($this->envprefix . $field, $values[$field]);
+            }
+        }
+    }
+
     /** Wrappers around Env::i/s/..., to add envprefix
      */
     public function s($key, $def = '')
     {
-        return Env::s($this->envprefix . $key, $def);
+        if ($this->fake_env) {
+            return $this->fake_env->s($key, $def);
+        } else {
+            return Env::s($this->envprefix . $key, $def);
+        }
     }
 
     public function t($key, $def = '')
     {
-        return Env::t($this->envprefix . $key, $def);
+        if ($this->fake_env) {
+            return $this->fake_env->t($key, $def);
+        } else {
+            return Env::t($this->envprefix . $key, $def);
+        }
     }
 
     public function i($key, $def = 0)
     {
-        return Env::i($this->envprefix . $key, $def);
+        if ($this->fake_env) {
+            return $this->fake_env->i($key, $def);
+        } else {
+            return Env::i($this->envprefix . $key, $def);
+        }
     }
 
     public function v($key, $def = null)
     {
-        return Env::v($this->envprefix . $key, $def);
+        if ($this->fake_env) {
+            return $this->fake_env->v($key, $def);
+        } else {
+            return Env::v($this->envprefix . $key, $def);
+        }
     }
 
     public function b($key, $def = false)
     {
-        return Env::b($this->envprefix . $key, $def);
+        if ($this->fake_env) {
+            return $this->fake_env->b($key, $def);
+        } else {
+            return Env::b($this->envprefix . $key, $def);
+        }
     }
 
     public function has($key)
     {
-        return Env::has($this->envprefix . $key);
+        if ($this->fake_env) {
+            return $this->fake_env->has($key);
+        } else {
+            return Env::has($this->envprefix . $key);
+        }
     }
 
     public function blank($key, $strict = false)
     {
-        return Env::blank($key, $strict);
+        if ($this->fake_env) {
+            return $this->fake_env->blank($key, $strict);
+        } else {
+            return Env::blank($key, $strict);
+        }
     }
 
     public function hasAlnum($key)
@@ -166,6 +321,7 @@ class UFB_QuickSearch extends UserFilterBuilder
     {
         $fields = array(
             new UFBF_Quick('quick', 'Recherche rapide'),
+            new UFBF_NotRegistered('nonins', 'Non inscrits'),
         );
         parent::__construct($fields, $envprefix);
     }
@@ -175,33 +331,46 @@ class UFB_QuickSearch extends UserFilterBuilder
 // {{{ class UFB_AdvancedSearch
 class UFB_AdvancedSearch extends UserFilterBuilder
 {
-    public function __construct($envprefix = '')
+    /** Create a UFB_AdvancedSearch.
+     * @param $include_admin Whether to include 'admin-only' fields
+     * @param $include_ax Whether to include 'ax-only' fields
+     * @param $envprefix Optional prefix for form field names.
+     */
+    public function __construct($include_admin = false, $include_ax = false, $envprefix = '')
     {
         $fields = array(
-            new UFBF_Name('name', 'Nom'),
-            new UFBF_Promo('promo1', 'Promotion', 'egal1'),
-            new UFBF_Promo('promo2', 'Promotion', 'egal2'),
+            new UFBF_Name('name', 'Nom', 'name_type'),
+            new UFBF_Promo('promo1', 'Promotion', 'egal1', 'edu_type'),
+            new UFBF_Promo('promo2', 'Promotion', 'egal2', 'edu_type'),
             new UFBF_Sex('woman', 'Sexe'),
             new UFBF_Registered('subscriber', 'Inscrit'),
+            new UFBF_HasEmailRedirect('has_email_redirect', 'A une redirection active'),
             new UFBF_Dead('alive', 'En vie'),
 
-            new UFBF_Town('city', 'Ville / Code Postal'),
-            new UFBF_Country('countryTxt', 'country', 'Pays'),
-            new UFBF_AdminArea('region', 'Région'),
+            new UFBF_AddressIndex('postal_code', 'Code postal', 'POSTALCODES'),
+            new UFBF_AddressIndex('administrative_area_level_3', 'Canton', 'ADMNISTRATIVEAREAS3'),
+            new UFBF_AddressIndex('administrative_area_level_2', 'Département', 'ADMNISTRATIVEAREAS2'),
+            new UFBF_AddressIndex('administrative_area_level_1', 'Région', 'ADMNISTRATIVEAREAS1'),
+            new UFBF_AddressMixed('locality_text', 'locality', 'Ville', 'LOCALITIES'),
+            new UFBF_AddressMixed('country_text', 'country', 'Pays', 'COUNTRIES'),
 
             new UFBF_JobCompany('entreprise', 'Entreprise'),
             new UFBF_JobDescription('jobdescription', 'Fonction'),
             new UFBF_JobCv('cv', 'CV'),
             new UFBF_JobTerms('jobterm', 'Mots-clefs'),
 
-            new UFBF_Nationality('nationaliteTxt', 'nationalite', 'Nationalité'),
-            new UFBF_Binet('binetTxt', 'binet', 'Binet'),
-            new UFBF_Group('groupexTxt', 'groupex', 'Groupe X'),
-            new UFBF_Section('sectionTxt', 'section', 'Section'),
+            new UFBF_OriginCorps('origin_corps', 'Corps d\'origine'),
+            new UFBF_CurrentCorps('current_corps', 'Corps actuel'),
+            new UFBF_CorpsRank('corps_rank', 'Grade'),
 
-            new UFBF_EducationSchool('schoolTxt', 'school', "École d'application"),
-            new UFBF_EducationDegree('diplomaTxt', 'diploma', 'Diplôme'),
-            new UFBF_EducationField('fieldTxt', 'field', "Domaine d'études"),
+            new UFBF_Nationality('nationalite_text', 'nationalite', 'Nationalité'),
+            new UFBF_Binet('binet_text', 'binet', 'Binet'),
+            new UFBF_Group('groupex_text', 'groupex', 'Groupe X'),
+            new UFBF_Section('section_text', 'section', 'Section'),
+
+            new UFBF_EducationSchool('school_text', 'school', "École d'application"),
+            new UFBF_EducationDegree('diploma_text', 'diploma', 'Diplôme'),
+            new UFBF_EducationField('field_text', 'field', "Domaine d'études"),
 
             new UFBF_Comment('free', 'Commentaire'),
             new UFBF_Phone('phone_number', 'Téléphone'),
@@ -209,6 +378,11 @@ class UFB_AdvancedSearch extends UserFilterBuilder
 
             new UFBF_Mentor('only_referent', 'Référent'),
         );
+
+        if ($include_admin || $include_ax) {
+            $fields[] = new UFBF_SchoolIds('schoolid_ax', 'Matricule AX', UFC_SchoolId::AX);
+        }
+
         parent::__construct($fields, $envprefix);
     }
 }
@@ -229,6 +403,60 @@ class UFB_MentorSearch extends UserFilterBuilder
 }
 // }}}
 
+// {{{ class UFB_DeltaTenSearch
+class UFB_DeltaTenSearch extends UserFilterBuilder
+{
+    public function __construct($envprefix = '')
+    {
+        $fields = array(
+            new UFBF_DeltaTenMessage('deltaten_message'),
+
+            new UFBF_AddressIndex('administrative_area_level_2', 'Département', 'ADMNISTRATIVEAREAS2'),
+            new UFBF_AddressIndex('administrative_area_level_1', 'Région', 'ADMNISTRATIVEAREAS1'),
+            new UFBF_AddressMixed('locality_text', 'locality', 'Ville', 'LOCALITIES'),
+            new UFBF_AddressMixed('country_text', 'country', 'Pays', 'COUNTRIES'),
+
+            new UFBF_EducationSchool('schoolTxt', 'school', "École d'application"),
+            new UFBF_EducationDegree('diplomaTxt', 'diploma', 'Diplôme'),
+            new UFBF_EducationField('fieldTxt', 'field', "Domaine d'études"),
+
+            new UFBF_JobCompany('entreprise', 'Entreprise'),
+            new UFBF_JobDescription('jobdescription', 'Fonction'),
+            new UFBF_JobTerms('jobterm', 'Mots-clefs'),
+
+            new UFBF_Nationality('nationaliteTxt', 'nationalite', 'Nationalité'),
+            new UFBF_Binet('binetTxt', 'binet', 'Binet'),
+            new UFBF_Group('groupexTxt', 'groupex', 'Groupe X'),
+            new UFBF_Section('sectionTxt', 'section', 'Section'),
+            new UFBF_Sex('woman', 'Sexe'),
+        );
+        parent::__construct($fields, $envprefix);
+    }
+}
+// }}}
+
+// {{{ class UFB_NewsLetter
+class UFB_NewsLetter extends UserFilterBuilder
+{
+    const FIELDS_PROMO = 'promo';
+    const FIELDS_AXID = 'axid';
+    const FIELDS_GEO = 'geo';
+
+    public function __construct($flags, $envprefix = '')
+    {
+        $fields = array();
+        if ($flags->hasFlag(self::FIELDS_PROMO)) {
+            $fields[] = new UFBF_Promo('promo1', 'Promotion', 'egal1', 'edu_type');
+            $fields[] = new UFBF_Promo('promo2', 'Promotion', 'egal2', 'edu_type');
+        }
+        if ($flags->hasFlag(self::FIELDS_AXID)) {
+            $fields[] = new UFBF_SchoolIds('axid', 'Matricule AX', UFC_SchoolId::AX);
+        }
+        parent::__construct($fields, $envprefix);
+    }
+}
+// }}}
+
 // {{{ class UFB_Field
 abstract class UFB_Field
 {
@@ -263,12 +491,12 @@ abstract class UFB_Field
         return false;
     }
 
-    public function apply(UserFilterBuilder &$ufb) {
+    public function apply(UserFilterBuilder $ufb) {
         if (!$this->check($ufb)) {
             return false;
         }
 
-        if (!$this->empty) {
+        if (!$this->isEmpty()) {
             $ufc = $this->buildUFC($ufb);
             if ($ufc != null) {
                 $ufb->addCond($ufc);
@@ -284,15 +512,26 @@ abstract class UFB_Field
 
     /** Create the UFC associated to the field; won't be called
      * if the field is "empty"
-     * @param &$ufb UFB to which fields must be added
+     * @param $ufb UFB to which fields must be added
      * @return UFC
      */
-    abstract protected function buildUFC(UserFilterBuilder &$ufb);
+    abstract protected function buildUFC(UserFilterBuilder $ufb);
 
     /** This function is intended to run consistency checks on the value
      * @return boolean Whether the input is valid
      */
-    abstract protected function check(UserFilterBuilder &$ufb);
+    abstract protected function check(UserFilterBuilder $ufb);
+
+    // Simple form interface
+
+    /** Retrieve a list of env field names used by that field
+     * their values will be recorded when saving the 'search' and used to prefill the form
+     * when needed.
+     */
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield);
+    }
 }
 // }}}
 
@@ -309,7 +548,7 @@ abstract class UFBF_Text extends UFB_Field
         $this->maxlength      = $maxlength;
     }
 
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfield)) {
             $this->empty = true;
@@ -346,7 +585,7 @@ abstract class UFBF_Range extends UFB_Field
         $this->max = $max;
     }
 
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfield)) {
             $this->empty = true;
@@ -369,7 +608,7 @@ abstract class UFBF_Range extends UFB_Field
  */
 abstract class UFBF_Index extends UFB_Field
 {
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfield)) {
             $this->empty = true;
@@ -394,7 +633,7 @@ abstract class UFBF_Enum extends UFB_Field
         $this->strict = $strict;
     }
 
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfield)) {
             $this->empty = true;
@@ -417,7 +656,7 @@ abstract class UFBF_Enum extends UFB_Field
 // {{{ class UFBF_Bool
 abstract class UFBF_Bool extends UFB_Field
 {
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfield)) {
             $this->empty = true;
@@ -447,7 +686,7 @@ abstract class UFBF_Mixed extends UFB_Field
         $this->envfieldindex = $envfieldindex;
     }
 
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfieldindex) && !$ufb->hasAlnum($this->envfield)) {
             $this->empty = true;
@@ -472,13 +711,18 @@ abstract class UFBF_Mixed extends UFB_Field
         }
         return true;
     }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfieldindex, $this->envfield);
+    }
 }
 // }}}
 
 // {{{ class UFBF_Quick
 class UFBF_Quick extends UFB_Field
 {
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
         if ($ufb->blank($this->envfield)) {
             $this->empty = true;
@@ -490,7 +734,7 @@ class UFBF_Quick extends UFB_Field
         return true;
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
 
         $r = $s = $this->val;
@@ -508,30 +752,25 @@ class UFBF_Quick extends UFB_Field
         /** Name
          */
         $s = preg_replace('!\d+!', ' ', $s);
-        $strings = preg_split("![^a-zA-Z%]+!",$s, -1, PREG_SPLIT_NO_EMPTY);
+        $strings = preg_split("![^a-z%]+!i", $s, -1, PREG_SPLIT_NO_EMPTY);
+        foreach ($strings as $key => $string) {
+            if (strlen($string) < 2) {
+                unset($strings[$key]);
+            }
+        }
         if (count($strings) > 5) {
             Platal::page()->trigWarning("Tu as indiqué trop d'éléments dans ta recherche, seuls les 5 premiers seront pris en compte");
             $strings = array_slice($strings, 0, 5);
         }
 
         if (count($strings)) {
-            if (S::logged()) {
+            if (S::user() != null && S::user()->checkPerms('directory_private')) {
                 $flags = array();
             } else {
                 $flags = array('public');
             }
-            if ($ufb->b('with_soundex')) {
-                $soundex = true;
-                $st = array();
-                foreach ($strings as $string) {
-                    $st[] = soundex_fr($string);
-                }
-            } else {
-                $soundex = false;
-                $st = $strings;
-            }
             $exact =$ufb->b('exact');
-            $conds->addChild(new UFC_NameTokens($st, $flags, $soundex, $exact));
+            $conds->addChild(new UFC_NameTokens($strings, $flags, $ufb->b('with_soundex'), $exact));
 
             $ufb->addOrder(new UFO_Score());
         }
@@ -540,29 +779,30 @@ class UFBF_Quick extends UFB_Field
          */
         $s = preg_replace('! *- *!', '-', $r);
         $s = preg_replace('!([<>]) *!', ' \1', $s);
-        $s = preg_replace('![^0-9\-><]!', ' ', $s);
+        $s = preg_replace('![^0-9xmd\-><]!i', ' ', $s);
         $s = preg_replace('![<>\-] !', '', $s);
-        $ranges = preg_split('! +!', $s, -1, PREG_SPLIT_NO_EMPTY);
+        $ranges = preg_split('! +!', strtolower($s), -1, PREG_SPLIT_NO_EMPTY);
+        $grades = array('' => UserFilter::GRADE_ING, 'x' => UserFilter::GRADE_ING, 'm' => UserFilter::GRADE_MST, 'd' => UserFilter::GRADE_PHD);
         foreach ($ranges as $r) {
-            if (preg_match('!^\d{4}$!', $r)) {
-                $conds->addChild(new UFC_Promo('=', UserFilter::DISPLAY, 'X' . $r));
-            } elseif (preg_match('!^(\d{4})-(\d{4})$!', $r, $matches)) {
-                $p1=min(intval($matches[1]), intval($matches[2]));
-                $p2=max(intval($matches[1]), intval($matches[2]));
+            if (preg_match('!^([xmd]?)(\d{4})$!', $r, $matches)) {
+                $conds->addChild(new UFC_Promo('=', $grades[$matches[1]], $matches[2]));
+            } elseif (preg_match('!^([xmd]?)(\d{4})-\1(\d{4})$!', $r, $matches)) {
+                $p1 = min(intval($matches[2]), intval($matches[3]));
+                $p2 = max(intval($matches[2]), intval($matches[3]));
                 $conds->addChild(new PFC_And(
-                    new UFC_Promo('>=', UserFilter::DISPLAY, 'X' . $p1),
-                    new UFC_Promo('<=', UserFilter::DISPLAY, 'X' . $p2)
+                    new UFC_Promo('>=', $grades[$matches[1]], $p1),
+                    new UFC_Promo('<=', $grades[$matches[1]], $p2)
                 ));
-            } elseif (preg_match('!^<(\d{4})!', $r, $matches)) {
-                $conds->addChild(new UFC_Promo('<=', UserFilter::DISPLAY, 'X' . $matches[1]));
-            } elseif (preg_match('!^>(\d{4})!', $r, $matches)) {
-                $conds->addChild(new UFC_Promo('>=', UserFilter::DISPLAY, 'X' . $matches[1]));
+            } elseif (preg_match('!^<([xmd]?)(\d{4})!', $r, $matches)) {
+                $conds->addChild(new UFC_Promo('<=', $grades[$matches[1]], $matches[2]));
+            } elseif (preg_match('!^>([xmd]?)(\d{4})!', $r, $matches)) {
+                $conds->addChild(new UFC_Promo('>=', $grades[$matches[1]], $matches[2]));
             }
         }
 
         /** Phone number
          */
-        $t = preg_replace('!(\d{4}-\d{4}|>\d{4}|<\d{4})!', '', $s);
+        $t = preg_replace('!([xmd]?\d{4}-|>|<|)[xmd]?\d{4}!i', '', $s);
         $t = preg_replace('![<>\- ]!', '', $t);
         if (strlen($t) > 4) {
             $conds->addChild(new UFC_Phone($t));
@@ -573,25 +813,105 @@ class UFBF_Quick extends UFB_Field
 }
 // }}}
 
+// {{{ class UFBF_SchoolIds
+class UFBF_SchoolIds extends UFB_Field
+{
+    // One of UFC_SchoolId types
+    protected $type;
+    protected $reversed_envfield;
+    protected $reversed = false;
+
+    public function __construct($envfield, $formtext, $type = UFC_SchoolId::AX, $reversed_envfield = '')
+    {
+        parent::__construct($envfield, $formtext);
+        $this->type = $type;
+        if ($reversed_envfield == '') {
+            $reversed_envfield = $envfield . '_reversed';
+        }
+        $this->reversed_envfield = $reversed_envfield;
+    }
+
+    protected function check(UserFilterBuilder $ufb)
+    {
+        if ($ufb->blank($this->envfield)) {
+            $this->empty = true;
+            return true;
+        }
+
+        $value = $ufb->t($this->envfield);
+        $values = explode("\n", $value);
+        $ids = array();
+        foreach ($values as $val) {
+            $val = trim($val);
+            if (preg_match('/^[0-9A-Z]{0,8}$/', $val)) {
+                $ids[] = $val;
+            }
+        }
+        if (count($ids) == 0) {
+            return $this->raise("Le champ %s ne contient aucune valeur valide.");
+        }
+
+        $this->reversed = $ufb->b($this->reversed_envfield);
+        $this->val = $ids;
+        return true;
+    }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->reversed_envfield);
+    }
+
+    protected function buildUFC(UserFilterBuilder $ufb)
+    {
+        $ufc = new UFC_SchoolId($this->type, $this->val);
+        if ($this->reversed) {
+            return new PFC_Not($ufc);
+        } else {
+            return $ufc;
+        }
+    }
+}
+// }}}
+
 // {{{ class UFBF_Name
 class UFBF_Name extends UFBF_Text
 {
-    protected function check(UserFilterBuilder &$ufb)
+    private $envfieldtype;
+    private $type;
+
+    public function __construct($envfield, $formtext = '', $envfieldtype)
+    {
+        parent::__construct($envfield, $formtext);
+        $this->envfieldtype = $envfieldtype;
+    }
+
+    protected function check(UserFilterBuilder $ufb)
     {
         if (!parent::check($ufb)) {
             return false;
         }
 
-        $this->val = preg_split('/[[:space:]]/', $this->val);
+        require_once 'name.func.inc.php';
+
+        $this->val = split_name_for_search($this->val);
         if (count($this->val) == 0) {
             $this->empty = true;
         }
+        $this->type = $ufb->v($this->envfieldtype);
+        if (!in_array($this->type, array('', 'lastname', 'firstname', 'nickname'))) {
+            return $this->raise("Le critère {$this->type} n'est pas valide pour le champ %s");
+        }
         return true;
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
-        return new UFC_NameTokens($this->val, array(), $ufb->b('with_soundex'), $ufb->b('exact'));
+        return new UFC_NameTokens($this->val, array(), $ufb->b('with_soundex'), $ufb->b('exact'), $this->type);
+    }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->envfieldtype);
     }
 }
 // }}}
@@ -600,24 +920,33 @@ class UFBF_Name extends UFBF_Text
 class UFBF_Promo extends UFB_Field
 {
     private static $validcomps = array('<', '<=', '=', '>=', '>');
+    private static $validtypes = array(UserFilter::GRADE_ING, UserFilter::GRADE_PHD, UserFilter::GRADE_MST);
     private $comp;
+    private $type;
     private $envfieldcomp;
+    private $envfieldtype;
 
-    public function __construct($envfield, $fromtext = '', $envfieldcomp)
+    public function __construct($envfield, $formtext = '', $envfieldcomp, $envfieldtype)
     {
-        parent::__construct($envfield, $fromtext);
+        parent::__construct($envfield, $formtext);
         $this->envfieldcomp = $envfieldcomp;
+        $this->envfieldtype = $envfieldtype;
     }
 
-    protected function check(UserFilterBuilder &$ufb)
+    protected function check(UserFilterBuilder $ufb)
     {
-        if ($ufb->blank($this->envfield) || $ufb->blank($this->envfieldcomp)) {
+        if ($ufb->blank($this->envfield) || $ufb->blank($this->envfieldcomp) || $ufb->blank($this->envfieldtype)) {
             $this->empty = true;
             return true;
         }
 
         $this->val  = $ufb->i($this->envfield);
         $this->comp = $ufb->v($this->envfieldcomp);
+        $this->type = $ufb->v($this->envfieldtype);
+
+        if (!in_array($this->type, self::$validtypes)) {
+            return $this->raise("Le critère {$this->type} n'est pas valide pour le champ %s");
+        }
 
         if (!in_array($this->comp, self::$validcomps)) {
             return $this->raise("Le critère {$this->comp} n'est pas valide pour le champ %s");
@@ -632,8 +961,13 @@ class UFBF_Promo extends UFB_Field
         return true;
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb) {
-        return new UFC_Promo($this->comp, UserFilter::GRADE_ING, $this->val);
+    protected function buildUFC(UserFilterBuilder $ufb) {
+        return new UFC_Promo($this->comp, $this->type, $this->val);
+    }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->envfieldcomp, $this->envfieldtype);
     }
 }
 // }}}
@@ -658,13 +992,29 @@ class UFBF_Sex extends UFBF_Enum
         }
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Sex(self::getVal($this->val));
     }
 }
 // }}}
 
+// {{{ class UFBF_NotRegistered
+// Simple field for selecting only alive, not registered users (for quick search)
+class UFBF_NotRegistered extends UFBF_Bool
+{
+    protected function buildUFC(UserFilterBuilder $ufb)
+    {
+        if ($this->val) {
+            return new PFC_And(
+                new PFC_Not(new UFC_Dead()),
+                new PFC_Not(new UFC_Registered())
+            );
+        }
+    }
+}
+// }}}
+
 // {{{ class UFBF_Registered
 class UFBF_Registered extends UFBF_Enum
 {
@@ -673,7 +1023,7 @@ class UFBF_Registered extends UFBF_Enum
         parent::__construct($envfield, $formtext, array(1, 2));
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         if ($this->val == 1) {
             return new UFC_Registered();
@@ -684,120 +1034,116 @@ class UFBF_Registered extends UFBF_Enum
 }
 // }}}
 
-// {{{ class UFBF_Dead
-class UFBF_Dead extends UFBF_Enum
+// {{{ class UFBF_HasEmailRedirect
+class UFBF_HasEmailRedirect extends UFBF_Enum
 {
     public function __construct($envfield, $formtext = '')
     {
         parent::__construct($envfield, $formtext, array(1, 2));
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         if ($this->val == 1) {
-            return new PFC_Not(new UFC_Dead());
+            return new UFC_HasEmailRedirect();
         } else if ($this->val == 2) {
-            return new UFC_Dead();
+            return new PFC_Not(new UFC_HasEmailRedirect());
         }
     }
 }
 // }}}
 
-// {{{ class UFBF_Town
-/** Retrieves a town, either from a postal code or a town name
- */
-class UFBF_Town extends UFBF_Text
+// {{{ class UFBF_Dead
+class UFBF_Dead extends UFBF_Enum
 {
-    const TYPE_TEXT = 1;
-    const TYPE_ZIP  = 2;
-    const TYPE_ANY  = 3;
-
-    private $type;
-    private $onlycurrentfield;
-
-    public function __construct($envfield, $formtext = '', $type = self::TYPE_ANY, $onlycurrentfield = 'only_current')
+    public function __construct($envfield, $formtext = '')
     {
-        $this->type = $type;
-        $this->onlycurrentfield = $onlycurrentfield;
-        parent::__construct($envfield, $formtext, 2, 30);
+        parent::__construct($envfield, $formtext, array(1, 2));
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
-        if ($ufb->isOn($this->onlycurrentfield)) {
-            $flags = UFC_Address::FLAG_CURRENT;
-        } else {
-            $flags = UFC_Address::FLAG_ANY;
-        }
-
-        if (preg_match('/[0-9]/', $this->val)) {
-            if ($this->type & self::TYPE_ZIP) {
-                return new UFC_AddressField($this->val, UFC_AddressField::FIELD_ZIPCODE, UFC_Address::TYPE_ANY, $flags);
-            } else {
-                return new PFC_False();
-            }
-        } else {
-            $byname = new UFC_AddressText(null, XDB::WILDCARD_CONTAINS, UFC_Address::TYPE_ANY, $flags, null, $this->val);
-            $byzip  = new UFC_AddressField($this->val, UFC_AddressField::FIELD_ZIPCODE, UFC_Address::TYPE_ANY, $flags);
-            if ($this->type & self::TYPE_ANY) {
-                return new PFC_Or($byname, $byzip);
-            } else if ($this->type & self::TYPE_TEXT) {
-                return $byname;
-            } else {
-                return $byzip;
-            }
+        if ($this->val == 1) {
+            return new PFC_Not(new UFC_Dead());
+        } else if ($this->val == 2) {
+            return new UFC_Dead();
         }
     }
 }
 // }}}
 
-// {{{ class UFBF_Country
-class UFBF_Country extends UFBF_Mixed
+// {{{ class UFBF_AddressMixed
+class UFBF_AddressMixed extends UFBF_Mixed
 {
-    protected $direnum = DirEnum::COUNTRIES;
     protected $onlycurrentfield;
+    protected $onlybestmailfield;
 
-    public function __construct($envfieldtext, $envfieldindex, $formtext = '', $onlycurrentfield = 'only_current')
+    public function __construct($envfieldtext, $envfieldindex, $formtext = '', $addressfield, $onlycurrentfield = 'only_current', $onlybestmailfield = 'only_best_mail')
     {
         parent::__construct($envfieldtext, $envfieldindex, $formtext);
         $this->onlycurrentfield = $onlycurrentfield;
+        $this->onlybestmailfield = $onlybestmailfield;
+        $this->direnum = constant('DirEnum::' . $addressfield);
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
+        $flags = UFC_Address::FLAG_NONE;
         if ($ufb->isOn($this->onlycurrentfield)) {
-            $flags = UFC_Address::FLAG_CURRENT;
-        } else {
+            $flags |= UFC_Address::FLAG_CURRENT;
+        }
+        if ($ufb->isOn($this->onlybestmailfield)) {
+            $flags |= UFC_Address::FLAG_BEST_MAIL;
+        }
+        if ($flags == UFC_Address::FLAG_NONE) {
             $flags = UFC_Address::FLAG_ANY;
         }
 
-        return new UFC_AddressField($this->val, UFC_AddressField::FIELD_COUNTRY, UFC_Address::TYPE_ANY, $flags);
+        return new UFC_AddressComponent($this->val, $this->envfieldindex, UFC_Address::TYPE_NON_HQ, $flags);
+    }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->envfieldindex, $this->onlycurrentfield, $this->onlybestmailfield);
     }
 }
 // }}}
 
-// {{{ class UFBF_AdminArea
-class UFBF_AdminArea extends UFBF_Index
+// {{{ class UFBF_AddressIndex
+class UFBF_AddressIndex extends UFBF_Index
 {
-    protected $direnum = DirEnum::ADMINAREAS;
+    protected $direnum;
     protected $onlycurrentfield;
+    protected $onlybestmailfield;
 
-    public function __construct($envfield, $formtext = '', $onlycurrentfield = 'only_current')
+    public function __construct($envfield, $formtext = '', $addressfield, $onlycurrentfield = 'only_current', $onlybestmailfield = 'only_best_mail')
     {
         parent::__construct($envfield, $formtext);
         $this->onlycurrentfield = $onlycurrentfield;
+        $this->onlybestmailfield = $onlybestmailfield;
+        $this->direnum = constant('DirEnum::' . $addressfield);
     }
 
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
+        $flags = UFC_Address::FLAG_NONE;
         if ($ufb->isOn($this->onlycurrentfield)) {
-            $flags = UFC_Address::FLAG_CURRENT;
-        } else {
+            $flags |= UFC_Address::FLAG_CURRENT;
+        }
+        if ($ufb->isOn($this->onlybestmailfield)) {
+            $flags |= UFC_Address::FLAG_BEST_MAIL;
+        }
+        if ($flags == UFC_Address::FLAG_NONE) {
             $flags = UFC_Address::FLAG_ANY;
         }
 
-        return new UFC_AddressField($this->val, UFC_AddressField::FIELD_ADMAREA, UFC_Address::TYPE_ANY, $flags);
+        return new UFC_AddressComponent($this->val, $this->envfield, UFC_Address::TYPE_NON_HQ, $flags);
+    }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->onlycurrentfield, $this->onlybestmailfield);
     }
 }
 // }}}
@@ -805,27 +1151,7 @@ class UFBF_AdminArea extends UFBF_Index
 // {{{ class UFBF_JobCompany
 class UFBF_JobCompany extends UFBF_Text
 {
-    private $onlymentorfield;
-
-    public function __construct($envfield, $formtext = '', $onlymentorfield = 'only_referent')
-    {
-        parent::__construct($envfield, $formtext);
-        $this->onlymentorfield = $onlymentorfield;
-    }
-
-    public function check(UserFilterBuilder &$ufb) {
-        if (parent::check($ufb)) {
-            # No company check for mentors
-           if ($ufb->isOn($this->onlymentorfield)) {
-                $this->empty = true;
-           }
-           return true;
-        } else {
-            return false;
-        }
-    }
-
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Job_Company(UFC_Job_Company::JOBNAME, $this->val);
     }
@@ -835,7 +1161,7 @@ class UFBF_JobCompany extends UFBF_Text
 // {{{ class UFBF_JobTerms
 class UFBF_JobTerms extends UFBF_Index
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Job_Terms($this->val);
     }
@@ -853,7 +1179,7 @@ class UFBF_JobDescription extends UFBF_Text
         $this->onlymentorfield = $onlymentorfield;
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         if ($ufb->isOn($this->onlymentorfield)) {
             return new UFC_Mentor_Expertise($this->val);
@@ -861,6 +1187,11 @@ class UFBF_JobDescription extends UFBF_Text
             return new UFC_Job_Description($this->val, UserFilter::JOB_USERDEFINED);
         }
     }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->onlymentorfield);
+    }
 }
 // }}}
 
@@ -875,7 +1206,7 @@ class UFBF_JobCv extends UFBF_Text
         $this->onlymentorfield = $onlymentorfield;
     }
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         if ($ufb->isOn($this->onlymentorfield)) {
             return new UFC_Mentor_Expertise($this->val);
@@ -883,6 +1214,11 @@ class UFBF_JobCv extends UFBF_Text
             return new UFC_Job_Description($this->val, UserFilter::JOB_CV);
         }
     }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->onlymentorfield);
+    }
 }
 // }}}
 
@@ -891,7 +1227,7 @@ class UFBF_Nationality extends UFBF_Mixed
 {
     protected $direnum = DirEnum::NATIONALITIES;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Nationality($this->val);
     }
@@ -903,7 +1239,7 @@ class UFBF_Binet extends UFBF_Mixed
 {
     protected $direnum = DirEnum::BINETS;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Binet($this->val);
     }
@@ -915,7 +1251,7 @@ class UFBF_Group extends UFBF_Mixed
 {
     protected $direnum = DirEnum::GROUPESX;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         if (count($this->val) == 1) {
             return new UFC_Group($this->val[0]);
@@ -935,7 +1271,7 @@ class UFBF_Section extends UFBF_Mixed
 {
     protected $direnum = DirEnum::SECTIONS;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Section($this->val);
     }
@@ -947,7 +1283,7 @@ class UFBF_EducationSchool extends UFBF_Mixed
 {
     protected $direnum = DirEnum::EDUSCHOOLS;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_EducationSchool($this->val);
     }
@@ -959,7 +1295,7 @@ class UFBF_EducationDegree extends UFBF_Mixed
 {
     protected $direnum = DirEnum::EDUDEGREES;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_EducationDegree($this->val);
     }
@@ -971,17 +1307,53 @@ class UFBF_EducationField extends UFBF_Mixed
 {
     protected $direnum = DirEnum::EDUFIELDS;
 
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_EducationField($this->val);
     }
 }
 // }}}
 
+// {{{ class UFBF_OriginCorps
+class UFBF_OriginCorps extends UFBF_Index
+{
+    protected $direnum = DirEnum::ORIGINCORPS;
+
+    protected function buildUFC(UserFilterBuilder $ufb)
+    {
+        return new UFC_Corps(null, $this->val, UFC_Corps::ORIGIN);
+    }
+}
+// }}}
+
+// {{{ class UFBF_CurrentCorps
+class UFBF_CurrentCorps extends UFBF_Index
+{
+    protected $direnum = DirEnum::CURRENTCORPS;
+
+    protected function buildUFC(UserFilterBuilder $ufb)
+    {
+        return new UFC_Corps(null, $this->val, UFC_Corps::CURRENT);
+    }
+}
+// }}}
+
+// {{{ class UFBF_CorpsRank
+class UFBF_CorpsRank extends UFBF_Index
+{
+    protected $direnum = DirEnum::CORPSRANKS;
+
+    protected function buildUFC(UserFilterBuilder $ufb)
+    {
+        return new UFC_Corps_Rank(null, $this->val);
+    }
+}
+// }}}
+
 // {{{ class UFBF_Comment
 class UFBF_Comment extends UFBF_Text
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Comment($this->val);
     }
@@ -991,7 +1363,7 @@ class UFBF_Comment extends UFBF_Text
 // {{{ class UFBF_Phone
 class UFBF_Phone extends UFBF_Text
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Phone($this->val);
     }
@@ -1010,7 +1382,7 @@ class UFBF_Networking extends UFBF_Text
         $this->networktypefield  = $networktypefield;
     }
 
-    public function check(UserFilterBuilder &$ufb)
+    public function check(UserFilterBuilder $ufb)
     {
         if (parent::check($ufb)) {
             $this->nwtype = $ufb->i($this->networktypefield);
@@ -1020,17 +1392,27 @@ class UFBF_Networking extends UFBF_Text
         }
     }
 
-    public function buildUFC(UserFilterBuilder &$ufb)
+    public function isEmpty()
+    {
+        return parent::isEmpty() || $this->nwtype == 0;
+    }
+
+    public function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Networking($this->nwtype, $this->val);
     }
+
+    public function getEnvFieldNames()
+    {
+        return array($this->envfield, $this->networktypefield);
+    }
 }
 // }}}
 
 // {{{ class UFBF_Mentor
 class UFBF_Mentor extends UFBF_Bool
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Mentor();
     }
@@ -1040,7 +1422,7 @@ class UFBF_Mentor extends UFBF_Bool
 // {{{ class UFBF_MentorCountry
 class UFBF_MentorCountry extends UFBF_Text
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Mentor_Country($this->val);
     }
@@ -1050,7 +1432,7 @@ class UFBF_MentorCountry extends UFBF_Text
 // {{{ class UFBF_Mentorterm
 class UFBF_MentorTerm extends UFBF_Index
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Mentor_Terms($this->val);
     }
@@ -1060,12 +1442,22 @@ class UFBF_MentorTerm extends UFBF_Index
 // {{{ class UFBF_MentorExpertise
 class UFBF_MentorExpertise extends UFBF_Text
 {
-    protected function buildUFC(UserFilterBuilder &$ufb)
+    protected function buildUFC(UserFilterBuilder $ufb)
     {
         return new UFC_Mentor_Expertise($this->val);
     }
 }
 // }}}
 
-// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+// {{{ class UFBF_DeltaTenMessage
+class UFBF_DeltaTenMessage extends UFBF_Text
+{
+    protected function buildUFC(UserFilterBuilder $ufb)
+    {
+        return new UFC_DeltaTen_Message($this->val);
+    }
+}
+// }}}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8:
 ?>