Add comments and folding to DirEnumeration
[platal.git] / include / ufbuilder.inc.php
CommitLineData
d9b3d712
RB
1<?php
2/***************************************************************************
21b67462 3 * Copyright (C) 2003-2010 Polytechnique.org *
d9b3d712
RB
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
21b67462
RB
22require_once 'directory.enums.inc.php';
23
24// {{{ class UserFilterBuilder
d9b3d712
RB
25class UserFilterBuilder
26{
27 private $envprefix;
28 private $fields;
29 private $valid = true;
30 private $ufc = null;
31
32 /** Constructor
33 * @param $fields An array of UFB_Field objects
34 * @param $envprefix Prefix to use for parts of the query
35 */
36 public function __construct($fields, $envprefix = '')
37 {
38 $this->fields = $fields;
39 $this->envprefix = $envprefix;
40 }
41
42 /** Builds the UFC; returns as soon as a field says it is invalid
43 */
44 private function buildUFC()
45 {
46 if ($this->ufc != null) {
47 return;
48 }
49 $this->ufc = new PFC_And();
50
51 foreach ($this->fields as $field) {
52 $this->valid = $field->apply(&$this);
53 if (!$this->valid) {
54 return;
55 }
56 }
57 }
58
59 public function addCond(PlFilterCondition &$cond)
60 {
61 $this->ufc->addChild($cond);
62 }
63
64 public function isValid()
65 {
66 $this->buildUFC();
67 return $this->valid;
68 }
69
70 /** Returns the built UFC
71 * @return The UFC, or PFC_False() if an error happened
72 */
73 public function &getUFC()
74 {
75 $this->buildUFC();
76 if ($this->valid) {
77 return $this->ufc;
78 } else {
79 return new PFC_False();
80 }
81 }
82
83 /** Wrappers around Env::i/s/..., to add envprefix
84 */
21b67462 85 public function s($key, $def = '') {
d9b3d712
RB
86 return trim(Env::s($this->envprefix . $key, $def));
87 }
88
21b67462 89 public function i($key, $def = 0) {
d9b3d712
RB
90 return intval(trim(Env::i($this->envprefix . $key, $def)));
91 }
92
21b67462 93 public function v($key, $def = null) {
d9b3d712
RB
94 return Env::v($this->envprefix . $key, $def);
95 }
96
97 public function has($key) {
21b67462 98 return (Env::has($this->envprefix . $key) && strlen($this->s($key, '')) > 0);
d9b3d712
RB
99 }
100}
21b67462 101// }}}
d9b3d712 102
21b67462
RB
103// {{{ class UFB_AdvancedSearch
104class UFB_AdvancedSearch extends UserFilterBuilder
105{
106 public function __construct($envprefix = '')
107 {
108 $fields = array(
109 new UFBF_Name('name', 'Nom'),
110 new UFBF_Promo('promo1', 'Promotion', 'egal1'),
111 new UFBF_Promo('promo2', 'Promotion', 'egal2'),
112 new UFBF_Sex('woman', 'Sexe'),
113 new UFBF_Registered('subscriber', 'Inscrit'),
114 new UFBF_Dead('alive', 'En vie'),
115
116 new UFBF_Town('city', 'Ville / Code Postal'),
117 new UFBF_Country('countryTxt', 'country', 'Pays'),
118 new UFBF_AdminArea('region', 'Région'),
119
120 new UFBF_JobCompany('entreprise', 'Entreprise'),
121 new UFBF_JobSector('sector', 'Poste'),
122 new UFBF_JobDescription('jobdescription', 'Fonction'),
123 new UFBF_JobCv('cv', 'CV'),
124
125 new UFBF_Nationality('nationaliteTxt', 'nationalite', 'Nationalité'),
126 new UFBF_Binet('binetTxt', 'binet', 'Binet'),
127 new UFBF_Group('groupexTxt', 'groupex', 'Groupe X'),
128 new UFBF_Section('sectionTxt', 'section', 'Section'),
129
130 new UFBF_Formation('schoolTxt', 'school', "École d'application"),
131 new UFBF_Diploma('diplomaTxt', 'diploma', 'Diplôme'),
132 new UFBF_StudiesDomain('fieldTxt', 'field', "Domaine d'études"),
133
134 new UFBF_Comment('free', 'Commentaire'),
135 );
136 parent::__construct($fields, $envprefix);
137 }
138}
139// }}}
140
141// {{{ class UFB_Field
d9b3d712
RB
142abstract class UFB_Field
143{
144 protected $envfield;
145 protected $formtext;
146
147 protected $empty = false;
148 protected $val = null;
149
150 /** Constructor
151 * @param $envfield Name of the field in the environment
152 * @param $formtext User-friendly name of that field
153 */
154 public function __construct($envfield, $formtext = '')
155 {
156 $this->envfield = $envfield;
157 if ($formtext != '') {
158 $this->formtext = $formtext;
159 } else {
160 $formtext = ucfirst($envfield);
161 }
162 }
163
164 /** Prints the given error message to the user, and returns false
165 * in order to be used as return $this->raise('ERROR');
166 *
167 * All %s in the $msg will be replaced with the formtext.
168 */
169 protected function raise($msg)
170 {
171 Platal::page()->trigError(str_replace('%s', $this->formtext, $msg));
172 return false;
173 }
174
175 public function apply(UserFilterBuilder &$ufb) {
176 if (!$this->check($ufb)) {
177 return false;
178 }
179
180 if (!$this->empty) {
181 $ufc = $this->buildUFC($ufb);
182 if ($ufc != null) {
183 $ufb->addCond($ufc);
184 }
185 }
186 return true;
187 }
188
189 /** Create the UFC associated to the field; won't be called
190 * if the field is "empty"
191 * @param &$ufb UFB to which fields must be added
192 * @return UFC
193 */
194 abstract protected function buildUFC(UserFilterBuilder &$ufb);
195
196 /** This function is intended to run consistency checks on the value
197 * @return boolean Whether the input is valid
198 */
199 abstract protected function check(UserFilterBuilder &$ufb);
200}
21b67462 201// }}}
d9b3d712 202
21b67462 203// {{{ class UFBF_Text
d9b3d712
RB
204abstract class UFBF_Text extends UFB_Field
205{
206 private $forbiddenchars;
207 private $minlength;
208 private $maxlength;
209
210 public function __construct($envfield, $formtext = '', $forbiddenchars = '', $minlength = 2, $maxlength = 255)
211 {
212 parent::__construct($envfield, $formtext);
213 $this->forbiddenchars = $forbiddenchars;
214 $this->minlength = $minlength;
215 $this->maxlength = $maxlength;
216 }
217
218 protected function check(UserFilterBuilder &$ufb)
219 {
220 if (!$ufb->has($this->envfield)) {
221 $this->empty = true;
222 return true;
223 }
224
225 $this->val = $ufb->s($this->envfield);
226 if (strlen($this->val) < $this->minlength) {
227 return $this->raise("Le champ %s est trop court (minimum {$this->minlength}).");
228 } else if (strlen($this->val) > $this->maxlength) {
229 return $this->raise("Le champ %s est trop long (maximum {$this->maxlength}).");
230 }
231 return true;
232 }
233}
21b67462 234// }}}
d9b3d712 235
21b67462 236// {{{ class UFBF_Range
d9b3d712
RB
237/** Subclass to use for fields which only allow integers within a range
238 */
239abstract class UFBF_Range extends UFB_Field
240{
241
242 private $min;
243 private $max;
244
245 public function __construct($envfield, $formtext = '', $min = 0, $max = 65535)
246 {
247 parent::__construct($envfield, $formtext);
248 $this->min = $min;
249 $this->max = $max;
250 }
251
252 protected function check(UserFilterBuilder &$ufb)
253 {
254 if (!$ufb->has($this->envfield)) {
255 $this->empty = true;
256 return true;
257 }
258
259 $this->val = $ufb->i($this->envfield);
260 if ($this->val < $this->min) {
261 return $this->raise("Le champs %s est inférieur au minimum ({$this->min}).");
262 } else if ($this->val > $this->max) {
263 return $this->raise("Le champ %s est supérieur au maximum ({$this->max}).");
264 }
265 return true;
266 }
267}
21b67462 268// }}}
d9b3d712 269
21b67462 270// {{{ class UFBF_Index
d9b3d712
RB
271/** Subclass to use for indexed fields
272 */
273abstract class UFBF_Index extends UFB_Field
274{
275 protected function check(UserFilterBuilder &$ufb)
276 {
277 if (!$ufb->has($this->envfield)) {
278 $this->empty = true;
279 }
280 return true;
281 }
282}
21b67462 283// }}}
d9b3d712 284
21b67462 285// {{{ class UFBF_Enum
d9b3d712
RB
286/** Subclass to use for fields whose value must belong to a specific set of values
287 */
288abstract class UFBF_Enum extends UFB_Field
289{
21b67462
RB
290 protected $allowedvalues;
291
292 public function __construct($envfield, $formtext = '', $allowedvalues = array(), $strict = false)
d9b3d712
RB
293 {
294 parent::__construct($envfield, $formtext);
295 $this->allowedvalues = $allowedvalues;
21b67462 296 $this->strict = $strict;
d9b3d712
RB
297 }
298
299 protected function check(UserFilterBuilder &$ufb)
300 {
301 if (!$ufb->has($this->envfield)) {
302 $this->empty = true;
303 return true;
304 }
305
306 $this->val = $ufb->v($this->envfield);
307 if (! in_array($this->val, $this->allowedvalues)) {
21b67462
RB
308 if ($this->strict) {
309 return $this->raise("La valeur {$this->val} n'est pas valide pour le champ %s.");
310 } else {
311 $this->empty = true;
312 }
d9b3d712
RB
313 }
314 return true;
315 }
316}
21b67462 317// }}}
d9b3d712 318
21b67462
RB
319// {{{ class UFBF_Bool
320abstract class UFBF_Bool extends UFB_Field
d9b3d712 321{
21b67462
RB
322 protected function check(UserFilterBuilder &$ufb)
323 {
324 if (!$ufb->has($this->envfield)) {
325 $this->empty = true;
326 return true;
327 }
328
329 $this->val = ($ufb->i($this->envfield) != 0);
330 return true;
331 }
332}
333// }}}
d9b3d712 334
21b67462
RB
335// {{{ class UFBF_Mixed
336/** A class for building UFBFs when the user can input either a text or an ID
337 */
338abstract class UFBF_Mixed extends UFB_Field
339{
340 /** Name of the DirEnum on which class is based
341 */
342 protected $direnum;
343
344 protected $envfieldindex;
345
346 public function __construct($envfieldtext, $envfieldindex, $formtext = '')
d9b3d712 347 {
21b67462
RB
348 parent::__construct($envfieldtext, $formtext);
349 $this->envfieldindex = $envfieldindex;
d9b3d712
RB
350 }
351
21b67462 352 protected function check(UserFilterBuilder &$ufb)
d9b3d712 353 {
21b67462
RB
354 if (!$ufb->has($this->envfieldindex) && !$ufb->has($this->envfield)) {
355 $this->empty = true;
356 return true;
357 }
358
359 if ($ufb->has($this->envfieldindex)) {
360 $index = $ufb->v($this->envfieldindex);
361 if (is_int($index)) {
362 $index = intval($index);
363 } else {
364 $index = strtoupper($index);
365 }
366 $this->val = array($index);
d9b3d712 367 } else {
21b67462
RB
368 $indexes = DirEnum::getIDs($this->direnum, $ufb->s($this->envfield),
369 $ufb->i('exact') ? DirEnumeration::MODE_EXACT : DirEnumeration::MODE_CONTAINS);
370 if (count($indexes) == 0) {
371 return false;
372 }
373 $this->val = $indexes;
d9b3d712 374 }
21b67462 375 return true;
d9b3d712
RB
376 }
377}
21b67462 378// }}}
d9b3d712 379
21b67462
RB
380// {{{ class UFBF_Name
381class UFBF_Name extends UFBF_Text
382{
383 protected function check(UserFilterBuilder &$ufb)
384 {
385 if (!parent::check($ufb)) {
386 return false;
387 }
388
389 $this->val = preg_split('/[[:space:]]/', $this->val);
390 if (count($this->val) == 0) {
391 $this->empty = true;
392 }
393 return true;
394 }
395
396 protected function buildUFC(UserFilterBuilder &$ufb)
397 {
398 return new UFC_NameTokens($this->val, array(), $ufb->i('with_soundex'), $ufb->i('exact'));
399 }
400}
401// }}}
402
403// {{{ class UFBF_Promo
d9b3d712
RB
404class UFBF_Promo extends UFB_Field
405{
406 private static $validcomps = array('<', '<=', '=', '>=', '>');
407 private $comp;
408 private $envfieldcomp;
409
410 public function __construct($envfield, $fromtext = '', $envfieldcomp)
411 {
412 parent::__construct($envfield, $fromtext);
413 $this->envfieldcomp = $envfieldcomp;
414 }
415
416 protected function check(UserFilterBuilder &$ufb)
417 {
418 if (!$ufb->has($this->envfield) || !$ufb->has($this->envfieldcomp)) {
419 $this->empty = true;
420 return true;
421 }
422
423 $this->val = $ubf->i($this->envfield);
424 $this->comp = $ubf->v($this->envfieldcomp);
425
426 if (!in_array($this->comp, self::$validcomps)) {
427 return $this->raise("Le critère {$this->comp} n'est pas valide pour le champ %s");
428 }
429
430 if (preg_match('/^[0-9]{2}$/', $this->val)) {
431 $this->val += 1900;
432 }
433 if ($this->val < 1900 || $this->val > 9999) {
434 return $this->raise("Le champ %s doit être une année à 4 chiffres.");
435 }
436 return true;
437 }
438
439 protected function buildUFC(UserFilterBuilder &$ufb) {
440 return new UFC_Promo($this->comp, UserFilter::DISPLAY, 'X' . $this->val);
441 }
442}
21b67462
RB
443// }}}
444
445// {{{ class UFBF_Sex
446class UFBF_Sex extends UFBF_Enum
447{
448 public function __construct($envfield, $formtext = '')
449 {
450 parent::__construct($envfield, $formtext, array(1, 2));
451 }
452
453 private static function getVal($id)
454 {
455 switch($id) {
456 case 1:
457 return User::GENDER_MALE;
458 break;
459 case 2:
460 return User::GENDER_FEMALE;
461 break;
462 }
463 }
464
465 protected function buildUFC(UserFilterBuilder &$ufb)
466 {
467 return new UFC_Sex(self::getVal($this->val));
468 }
469}
470// }}}
471
472// {{{ class UFBF_Registered
473class UFBF_Registered extends UFBF_Enum
474{
475 public function __construct($envfield, $formtext = '')
476 {
477 parent::__construct($envfield, $formtext, array(1, 2));
478 }
479
480 protected function buildUFC(UserFilterBuilder &$ufb)
481 {
482 if ($this->val == 1) {
483 return new UFC_Registered();
484 } else if ($this->val == 2) {
485 return new PFC_Not(UFC_Registered());
486 }
487 }
488}
489// }}}
d9b3d712 490
21b67462
RB
491// {{{ class UFBF_Dead
492class UFBF_Dead extends UFBF_Enum
493{
494 public function __construct($envfield, $formtext = '')
495 {
496 parent::__construct($envfield, $formtext, array(1, 2));
497 }
498
499 protected function buildUFC(UserFilterBuilder &$ufb)
500 {
501 if ($this->val == 1) {
502 return new PFC_Not(UFC_Dead());
503 } else if ($this->val == 2) {
504 return new UFC_Dead();
505 }
506 }
507}
508// }}}
509
510// {{{ class UFBF_Town
511/** Retrieves a town, either from a postal code or a town name
512 */
513class UFBF_Town extends UFBF_Text
514{
515 const TYPE_TEXT = 1;
516 const TYPE_ZIP = 2;
517 const TYPE_ANY = 3;
518
519 private $type;
520 public function __construct($envfield, $formtext = '', $type = self::TYPE_ANY)
521 {
522 $this->type = $type;
523 parent::__construct($envfield, $formtext, '', 2, 30);
524 }
525
526 protected function buildUFC(UserFilterBuilder &$ufb)
527 {
528 if (preg_match('/[0-9]/', $this->val)) {
529 if ($this->type & self::TYPE_ZIP) {
530 return new UFC_AddressField(UFC_Address::TYPE_ANY, UFC_Address::FLAG_ANY, null, null, null, null, $this->val);
531 } else {
532 return new PFC_False();
533 }
534 } else {
535 $byname = new UFC_AddressText(null, UFC_Address::CONTAINS, UFC_Address::TYPE_ANY, UFC_Address::FLAG_ANY, null, $this->val);
536 $byzip = new UFC_AddressField(UFC_Address::TYPE_ANY, UFC_Address::FLAG_ANY, null, null, null, null, $this->val);
537 if ($this->type & self::TYPE_ANY) {
538 return new PFC_Or($byname, $byzip);
539 } else if ($this->type & self::TYPE_TEXT) {
540 return $byname;
541 } else {
542 return $byzip;
543 }
544 }
545 }
546}
547// }}}
548
549// {{{ class UFBF_Country
550class UFBF_Country extends UFBF_Mixed
551{
552 protected $direnum = DirEnum::COUNTRIES;
553
554 protected function buildUFC(UserFilterBuilder &$ufb)
555 {
556 return new UFC_AddressField($this->val, UFC_AddressField::FIELD_COUNTRY, UFC_Address::TYPE_ANY, UFC_Address::FLAG_ANY);
557 }
558}
559// }}}
560
561// {{{ class UFBF_AdminArea
562class UFBF_AdminArea extends UFBF_Mixed
563{
564 protected $direnum = DirEnum::ADMINAREAS;
565
566 protected function buildUFC(UserFilterBuilder &$ufb)
567 {
568 return new UFC_AddressField($this->val, UFC_AddressField::FIELD_ADMAREA, UFC_Address::TYPE_ANY, UFC_Address::FLAG_ANY);
569 }
570}
571// }}}
572
573// {{{ class UFBF_JobCompany
574class UFBF_JobCompany extends UFBF_Text
575{
576 protected function buildUFC(UserFilterBuilder &$ufb)
577 {
578 return new UFC_Job_Company(UFC_Job_Company::JOBNAME, $this->val);
579 }
580}
581// }}}
582
583// {{{ class UFBF_JobSector
584class UFBF_JobSector extends UFBF_Mixed
585{
586 protected $direnum = DirEnum::SECTORS;
587
588 protected function buildUFC(UserFilterBuilder &$ufb)
589 {
590 return new UFC_Job_Sectorization($this->val, UserFilter::JOB_SUBSUBSECTOR);
591 }
592}
593// }}}
594
595// {{{ class UFBF_JobDescription
596class UFBF_JobDescription extends UFBF_Text
597{
598 protected function buildUFC(UserFilterBuilder &$ufb)
599 {
600 return new UFC_Job_Description($this->val, UserFilter::JOB_USERDEFINED);
601 }
602}
603// }}}
604
605// {{{ class UFBF_JobCv
606class UFBF_JobCv extends UFBF_Text
607{
608 protected function buildUFC(UserFilterBuilder &$ufb)
609 {
610 return new UFC_Job_Description($this->val, UserFilter::JOB_CV);
611 }
612}
613// }}}
614
615// {{{ class UFBF_Nationality
616class UFBF_Nationality extends UFBF_Mixed
617{
618 protected $direnum = DirEnum::NATIONALITIES;
619
620 protected function buildUFC(UserFilterBuilder &$ufb)
621 {
622 return new UFC_Nationality($this->val);
623 }
624}
625// }}}
626
627// {{{ class UFBF_Binet
628class UFBF_Binet extends UFBF_Mixed
629{
630 protected $direnum = DirEnum::BINETS;
631
632 protected function buildUFC(UserFilterBuilder &$ufb)
633 {
634 return new UFC_Binet($this->val);
635 }
636}
637// }}}
638
639// {{{ class UFBF_Group
640class UFBF_Group extends UFBF_Mixed
641{
642 protected $direnum = DirEnum::GROUPESX;
643
644 protected function buildUFC(UserFilterBuilder &$ufb)
645 {
646 if (count($this->val) == 1) {
647 return new UFC_Group($this->val[0]);
648 }
649
650 $or = new PFC_Or();
651 foreach ($this->val as $grp) {
652 $or->addChild(new UFC_Group($grp));
653 }
654 return $or;
655 }
656}
657// }}}
658
659// {{{ class UFBF_Section
660class UFBF_Section extends UFBF_Index
661{
662 protected $direnum = DirEnum::SECTIONS;
663
664 protected function buildUFC(UserFilterBuilder &$ufb)
665 {
666 return new UFC_Section($this->val);
667 }
668}
669// }}}
670
671// {{{ class UFBF_Formation
672class UFBF_Formation extends UFBF_Mixed
673{
674 protected $direnum = DirEnum::SCHOOLS;
675
676 protected function buildUFC(UserFilterBuilder &$ufb)
677 {
678 return new UFC_Formation($this->val);
679 }
680}
681// }}}
682
683// {{{ class UFBF_Diploma
684class UFBF_Diploma extends UFBF_Mixed
685{
686 protected $direnum = DirEnum::DEGREES;
687
688 protected function buildUFC(UserFilterBuilder &$ufb)
689 {
690 return new UFC_Diploma($this->val);
691 }
692}
693// }}}
694
695// {{{ class UFBF_StudiesDomain
696class UFBF_StudiesDomain extends UFBF_Mixed
697{
698 protected $direnum = DirEnum::STUDIESDOMAINS;
699
700 protected function buildUFC(UserFilterBuilder &$ufb)
701 {
702 return new UFC_StudyField($this->val);
703 }
704}
705// }}}
706
707// {{{ class UFBF_Comment
708class UFBF_Comment extends UFBF_Text
709{
710 protected function buildUFC(UserFilterBuilder &$ufb)
711 {
712 return new UFC_Comment($this->val);
713 }
714}
715// }}}
d9b3d712 716?>