Redefine DirEnum API, factor some code
[platal.git] / include / directory.enums.inc.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
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
22 // {{{ class DirEnum
23 /** This class stores all data for the different kinds of fields.
24 * It is only a dispatcher for the various DirEnum_XXX classes.
25 */
26 class DirEnum
27 {
28 /** Name of availables Enumerations
29 * Each of these consts contains the basename of the class (its full name
30 * being DE_$basename).
31 */
32 const NAMETYPES = 'nametypes';
33
34 const BINETS = 'binets';
35 const GROUPESX = 'groupesx';
36 const SECTIONS = 'sections';
37
38 const EDUSCHOOLS = 'educationschools';
39 const EDUDEGREES = 'educationdegrees';
40 const EDUFIELDS = 'educationfields';
41
42 const NATIONALITIES = 'nationalities';
43 const COUNTRIES = 'countries';
44 const ADMINAREAS = 'adminareas';
45 const LOCALITIES = 'localities';
46
47 const COMPANIES = 'companies';
48 const SECTORS = 'sectors';
49 const JOBDESCRIPTION = 'jobdescription';
50
51 const NETWORKS = 'networking';
52
53 static private $enumerations = array();
54
55 static private function init($type)
56 {
57 $cls = "DE_" . ucfirst($type);
58 self::$enumerations[$type] = new $cls();
59 }
60
61 /** Retrieves all options for a given type
62 * @param $type Type of enum for which options are requested
63 * @return Array of the results
64 */
65 static public function getOptions()
66 {
67 $args = func_get_args();
68 $type = array_shift($args);
69 if (!array_key_exists($type, self::$enumerations)) {
70 self::init($type);
71 }
72 $obj = self::$enumerations[$type];
73 if ($obj->capabilities & DirEnumeration::HAS_OPTIONS) {
74 return call_user_func_array(array($obj, 'getOptions'), $args);
75 } else {
76 return array();
77 }
78 }
79
80 /** Retrieves all options for a given type
81 * @param $type Type of enum for which options are requested
82 * @return PlIterator over the results
83 */
84 static public function getOptionsIter()
85 {
86 $args = func_get_args();
87 $type = array_shift($args);
88 if (!array_key_exists($type, self::$enumerations)) {
89 self::init($type);
90 }
91 $obj = self::$enumerations[$type];
92 if ($obj->capabilities & DirEnumeration::HAS_OPTIONS) {
93 return call_user_func_array(array($obj, 'getOptionsIter'), $args);
94 } else {
95 return PlIteratorUtils::fromArray(array());
96 }
97 }
98
99 /** Retrieves all options with number of profiles for autocompletion
100 * @param $type Type of enum for which options are requested
101 * @param $text Text to autocomplete
102 * @return PlIterator over the results
103 */
104 static public function getAutoComplete()
105 {
106 $args = func_get_args();
107 $type = array_shift($args);
108 if (!array_key_exists($type, self::$enumerations)) {
109 self::init($type);
110 }
111 $obj = self::$enumerations[$type];
112 if ($obj->capabilities & DirEnumeration::HAS_AUTOCOMP) {
113 return call_user_func_array(array($obj, 'getAutoComplete'), $args);
114 } else {
115 return PlIteratorUtils::fromArray(array());
116 }
117 }
118
119 /** Retrieves a list of IDs for a given type
120 * @param $type Type of enum for which IDs are requested
121 * @param $text Text to search in enum valuees
122 * @param $mode Mode of search for those IDs (prefix/suffix/infix)
123 */
124 static public function getIDs()
125 {
126 $args = func_get_args();
127 $type = array_shift($args);
128 if (!array_key_exists($type, self::$enumerations)) {
129 self::init($type);
130 }
131 $obj = self::$enumerations[$type];
132 if ($obj->capabilities & DirEnumeration::HAS_OPTIONS) {
133 return call_user_func_array(array($obj, 'getIDs'), $args);
134 } else {
135 return array();
136 }
137 }
138 }
139 // }}}
140
141 // {{{ class DirEnumeration
142 abstract class DirEnumeration
143 {
144 const AUTOCOMPLETE_LIMIT = 11;
145
146 const HAS_OPTIONS = 0x001;
147 const HAS_AUTOCOMP = 0x002;
148
149 public $capabilities = 0x003; // self::HAS_OPTIONS | self::HAS_AUTOCOMP;
150
151 /** An internal array of ID => optionTxt
152 */
153 protected $options = null;
154
155 /** Description of the MySQL storage of the fields
156 */
157 protected $idfield = 'id';
158 protected $valfield = 'text';
159 protected $valfield2 = null;
160 protected $from;
161 protected $join = '';
162 protected $where = '';
163
164 /** Fields for autocompletion
165 */
166 protected $ac_join = ''; // Additional joins
167 protected $ac_where = null; // Additional where
168 protected $ac_beginwith = true; // Whether to search for 'x%' or for '%x%'
169 protected $ac_unique; // Which field is to be taken as unique
170 protected $ac_distinct = true; // Whether we want to keep only distinct valfield value
171 protected $ac_withid = true; // Do we want to fetch id too ?
172
173 protected function _fetchOptions()
174 {
175 if (is_null($this->options)) {
176 $this->loadOptions();
177 }
178 }
179
180 public function getOptions()
181 {
182 $this->_fetchOptions();
183 return $this->options;
184 }
185
186 public function getOptionsIter()
187 {
188 return PlIteratorUtils::fromArray(self::expandArray($this->getOptions()), 1, true);
189 }
190
191 // {{{ function getIDs
192 /** Retrieves possible IDs for given text
193 * @param $text Text to search for IDs
194 * @param $mode Mode of search (PREFIX, SUFFIX, CONTAINS)
195 * @return An array of matching IDs ; if empty, input should be considered invalid
196 */
197 public function getIDs($text, $mode)
198 {
199 if ($mode == XDB::WILDCARD_EXACT) {
200 $options = $this->getOptions();
201 return array_keys($options, $text);
202 } else {
203 if ($this->where == null) {
204 $where = 'WHERE ';
205 } else {
206 $where = $this->where . ' AND ';
207 }
208 $conds = array();
209 $conds[] = $this->valfield . XDB::formatWildcards($mode, $text);
210 if ($this->valfield2 != null) {
211 $conds[] = $this->valfield2 . XDB::formatWildcards($mode, $text);
212 }
213 $where .= '(' . implode(' OR ', $conds) . ')';
214
215 return XDB::fetchColumn('SELECT ' . $this->idfield . '
216 FROM ' . $this->from . '
217 ' . $this->join . '
218 ' . $where . '
219 GROUP BY ' . $this->idfield);
220 }
221 }
222 // }}}
223
224 private function mkTests($field, $text)
225 {
226 $tests = array();
227 $tests[] = $field . XDB::formatWildcards(XDB::WILDCARD_PREFIX, $text);
228 if (!$this->ac_beginwith) {
229 $tests[] = $field . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, ' ' . $text);
230 $tests[] = $field . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, '-' . $text);
231 }
232 return $tests;
233 }
234
235 static protected function expandArray(array $tab, $keyname = 'id', $valname = 'field')
236 {
237 $res = array();
238 foreach ($tab as $key => $val) {
239 $res[$key] = array(
240 $keyname => $key,
241 $valname => $val,
242 );
243 }
244 return $res;
245 }
246
247 // {{{ function getAutoComplete
248 public function getAutoComplete($text)
249 {
250 $text = str_replace(array('%', '_'), '', $text);
251
252 if (is_null($this->ac_where) || $this->ac_where == '') {
253 $where = '';
254 } else {
255 $where = $this->ac_where . ' AND ';
256 }
257
258 $tests = $this->mkTests($this->valfield, $text);
259 if (!is_null($this->valfield2)) {
260 $tests = array_merge($tests, $this->mkTests($this->valfield2, $text));
261 }
262
263 $where .= '(' . implode(' OR ', $tests) . ')';
264
265 return XDB::iterator('SELECT ' . $this->valfield . ' AS field'
266 . ($this->ac_distinct ? (', COUNT(DISTINCT ' . $this->ac_unique . ') AS nb') : '')
267 . ($this->ac_withid ? (', ' . $this->idfield . ' AS id') : '') . '
268 FROM ' . $this->from . '
269 ' . $this->ac_join . '
270 WHERE ' . $where . '
271 GROUP BY ' . $this->valfield . '
272 ORDER BY ' . ($this->ac_distinct ? 'nb DESC' : $this->valfield) . '
273 LIMIT ' . self::AUTOCOMPLETE_LIMIT);
274 }
275 // }}}
276
277 // {{{ function loadOptions
278 /** The function used to load options
279 */
280 protected function loadOptions()
281 {
282 $this->options = XDB::fetchAllAssoc('id', 'SELECT ' . $this->valfield . ' AS field,
283 ' . $this->idfield . ' AS id
284 FROM ' . $this->from . '
285 ' . $this->join . '
286 ' . $this->where . '
287 GROUP BY ' . $this->valfield . '
288 ORDER BY ' . $this->valfield);
289 }
290 // }}}
291 }
292 // }}}
293
294 // {{{ class DE_WithSuboption
295 /** A class for DirEnum with possibility to select only suboptions for a given parameter (country, school, ...)
296 */
297 abstract class DE_WithSuboption extends DirEnumeration
298 {
299 protected $optfield;
300
301 protected $suboptions = null;
302
303 protected function loadOptions()
304 {
305 $opts = XDB::fetchAllAssoc('id', 'SELECT ' . $this->valfield . ' AS field,
306 ' . $this->optfield . ' AS subid,
307 ' . $this->idfield . ' AS id
308 FROM ' . $this->from . '
309 ' . $this->join . '
310 ' . $this->where . '
311 GROUP BY ' . $this->valfield . '
312 ORDER BY ' . $this->valfield);
313 $this->options = array();
314 $this->suboptions = array();
315 foreach ($opts as $id => $opt) {
316 $this->options[$id] = $opt['field'];
317 if (!array_key_exists($opt['subid'], $this->suboptions)) {
318 $this->suboptions[$opt['subid']] = array();
319 }
320 $this->suboptions[$opt['subid']][$id] = $opt['field'];
321 }
322 }
323
324 public function getOptions($subid = null)
325 {
326 $this->_fetchOptions();
327 if ($subid == null) {
328 return $this->options;
329 } else if (array_key_exists($subid, $this->suboptions)) {
330 return $this->suboptions[$subid];
331 } else {
332 return false;
333 }
334 }
335
336 public function getOptionsIter($subid = null)
337 {
338 return PlIteratorUtils::fromArray(self::expandArray($this->getOptions($subid)), 1, true);
339 }
340
341 public function getIDs($text, $mode, $subid = null)
342 {
343 if ($mode == XDB::WILDCARD_EXACT) {
344 $options = $this->getOptions($subid);
345 return array_keys($options, $text);
346 } else {
347 if ($this->where == null) {
348 $where = 'WHERE ';
349 } else {
350 $where = $this->where . ' AND ';
351 }
352 if ($subid != null && array_key_exists($subid, $this->suboptions)) {
353 $where .= XDB::format($this->optfield . ' = {?} AND ', $subid);
354 }
355
356 $conds = array();
357 $conds[] = $this->valfield . XDB::formatWildcards($mode, $text);
358 if ($this->valfield2 != null) {
359 $conds[] = $this->valfield2 . XDB::formatWildcards($mode, $text);
360 }
361 $where .= '(' . implode(' OR ', $conds) . ')';
362
363 return XDB::fetchColumn('SELECT ' . $this->idfield . '
364 FROM ' . $this->from . '
365 ' . $this->join . '
366 ' . $where . '
367 GROUP BY ' . $this->idfield);
368 }
369 }
370
371 public function getAutoComplete($text, $subid = null)
372 {
373 $text = str_replace(array('%', '_'), '', $text);
374
375 if (is_null($this->ac_where) || $this->ac_where == '') {
376 $where = '';
377 } else {
378 $where = $this->ac_where . ' AND ';
379 }
380
381 if ($subid != null && array_key_exists($subid, $this->suboptions)) {
382 $where .= XDB::format($this->optfield . ' = {?} AND ', $subid);
383 }
384
385 $tests = $this->mkTests($this->valfield, $text);
386 if (!is_null($this->valfield2)) {
387 $tests = array_merge($tests, $this->mkTests($this->valfield2, $text));
388 }
389
390 $where .= '(' . implode(' OR ', $tests) . ')';
391
392 return XDB::iterator('SELECT ' . $this->valfield . ' AS field'
393 . ($this->ac_distinct ? (', COUNT(DISTINCT ' . $this->ac_unique . ') AS nb') : '')
394 . ($this->ac_withid ? (', ' . $this->idfield . ' AS id') : '') . '
395 FROM ' . $this->from . '
396 ' . $this->ac_join . '
397 WHERE ' . $where . '
398 GROUP BY ' . $this->valfield . '
399 ORDER BY ' . ($this->ac_distinct ? 'nb DESC' : $this->valfield) . '
400 LIMIT ' . self::AUTOCOMPLETE_LIMIT);
401 }
402 }
403 // }}}
404
405 // {{{ class DE_NameTypes
406 // returns 'system' names ('lastname', 'lastname_marital', ...)
407 class DE_NameTypes extends DirEnumeration
408 {
409 public $capabilities = self::HAS_OPTIONS;
410
411 protected $from = 'profile_name_enum';
412 protected $valfield = 'type';
413 }
414 // }}}
415
416 /** GROUPS
417 */
418 // {{{ class DE_Binets
419 class DE_Binets extends DirEnumeration
420 {
421 protected $from = 'binets_def';
422
423 protected $ac_join = 'INNER JOIN binets_ins ON (binets_def.id = binets_ins.binet_id)';
424 protected $ac_unique = 'binets_ins.user_id';
425 }
426 // }}}
427
428 // {{{ class DE_Sections
429 class DE_Sections extends DirEnumeration
430 {
431 protected $from = 'sections';
432
433 protected $ac_join = 'INNER JOIN profiles ON (profiles.section = sections.id)';
434 protected $ac_unique = 'profiles.pid';
435 }
436 // }}}
437
438 // {{{ class DE_GroupesX
439 class DE_GroupesX extends DirEnumeration
440 {
441 protected $idfield = 'groups.id';
442 protected $valfield = 'groups.nom';
443 protected $valfield2 = 'groups.diminutif';
444 protected $from = 'groups';
445 protected $where = 'WHERE (cat = \'GroupesX\' OR cat = \'Institutions\') AND pub = \'public\'';
446
447 protected $ac_join = "INNER JOIN group_members ON (groups.id = memb.asso_id
448 AND (groups.cat = 'GroupesX' OR groups.cat = 'Institutions')
449 AND groups.pub = 'public')";
450 protected $ac_unique = 'group_members.uid';
451 }
452 // }}}
453
454 /** EDUCATION
455 */
456 // {{{ class DE_EducationSchools
457 class DE_EducationSchools extends DirEnumeration
458 {
459 protected $valfield = 'profile_education_enum.name';
460 protected $valfield2 = 'profile_education_enum.abbreviation';
461 protected $from = 'profile_education_enum';
462
463 protected $ac_join = 'INNER JOIN profile_education ON (profile_education.eduid = profile_education_enum.id)';
464 protected $ac_unique = 'profile_education.uid';
465 }
466 // }}}
467
468 // {{{ class DE_EducationDegrees
469 class DE_EducationDegrees extends DirEnumeration
470 {
471 public $capabilities = self::HAS_OPTIONS;
472
473 protected $idfield = 'profile_education_degree.degreeid';
474 protected $optfield = 'profile_education_degree.eduid';
475 protected $valfield = 'profile_education_degree_enum.degree';
476 protected $from = 'profile_education_degree_enum';
477 protected $join = 'INNER JOIN profile_education_degree ON (profile_education_degree.degreeid = profile_education_degree_enum.id)';
478
479 }
480 // }}}
481
482 // {{{ class DE_EducationFields
483 class DE_EducationFields extends DirEnumeration
484 {
485 protected $valfield = 'profile_education_field_enum.field';
486 protected $from = 'profile_education_field_enum';
487
488 protected $ac_join = 'INNER JOIN profile_education ON (profile_education.fieldid = profile_education_field_enum.id)';
489 protected $ac_unique = 'profile_education.uid';
490 }
491 // }}}
492
493 /** GEOLOC
494 */
495 // {{{ class DE_Nationalities
496 class DE_Nationalities extends DirEnumeration
497 {
498 protected $idfield = 'geoloc_countries.iso_3166_1_a2';
499 protected $valfield = 'geoloc_countries.nationalityFR';
500 protected $valfield2 = 'geoloc_countries.nationality';
501 protected $from = 'geoloc_countries';
502 protected $join = 'INNER JOIN profiles ON (geoloc_countries.iso_3166_1_a2 IN (profiles.nationality1, profiles.nationality2, profiles.nationality3))';
503
504 protected $ac_join = 'INNER JOIN profiles ON (geoloc_countries.iso_3166_1_a2 IN (profiles.nationality1, profiles.nationality2, profiles.nationality3))';
505 protected $ac_unique = 'profiles.pid';
506 }
507 // }}}
508
509 // {{{ class DE_Countries
510 class DE_Countries extends DirEnumeration
511 {
512 protected $idfield = 'geoloc_countries.iso_3166_1_a2';
513 protected $valfield = 'geoloc_countries.countryFR';
514 protected $valfield2 = 'geoloc_countries.country';
515 protected $from = 'geoloc_countries';
516
517 protected $ac_join = 'INNER JOIN profile_addresses ON (geoloc_countries.iso_3166_1_a2 = profile_addresses.countryFR';
518 protected $ac_unique = 'profile_addresses.pid';
519 }
520 // }}}
521
522 // {{{ class DE_AdminAreas
523 class DE_AdminAreas extends DE_WithSuboption
524 {
525 protected $idfield = 'geoloc_administrativeareas.id';
526 protected $optfield = 'geoloc_administrativeareas.country';
527 protected $valfield = 'geoloc_administrativeareas.name';
528 protected $from = 'geoloc_administrativeareas';
529
530 protected $ac_join = 'INNER JOIN profile_addresses ON (profile_addresses.administrativeAreaId = geoloc_administrativeareas.id)';
531 protected $ac_unique = 'profile_addresses.pid';
532 }
533 // }}}
534
535 // {{{ class DE_Localities
536 class DE_Localities extends DirEnumeration
537 {
538 protected $valfield = 'geoloc_localities.name';
539 protected $from = 'geoloc_localities';
540
541 protected $ac_join = 'profile_addresses ON (profile_addresses.localityID = geoloc_localities.id)';
542 protected $ac_unique = 'profile_addresses.pid';
543 }
544 // }}}
545
546 /** JOBS
547 */
548 // {{{ class DE_Companies
549 class DE_Companies extends DirEnumeration
550 {
551 protected $valfield = 'profile_job_enum.name';
552 protected $valfield2 = 'profile_job_enum.acronym';
553 protected $from = 'profile_job_enum';
554
555 protected $ac_join = 'INNER JOIN profile_job ON (profile_job.jobid = profile_job_enum.id)';
556 protected $ac_unique = 'profile_job.uid';
557 }
558 // }}}
559
560 // {{{ class DE_Sectors
561 class DE_Sectors extends DirEnumeration
562 {
563 protected $valfield = 'profile_job_sector_enum.name';
564 protected $from = 'profile_job_sector_enum';
565
566 protected $ac_join = 'INNER JOIN profile_job ON (profile_job_sector_enum.id = profile_job.sectorid)';
567 protected $ac_unique = 'profile_job.uid';
568 }
569 // }}}
570
571 // {{{ class DE_JobDescription
572 class DE_JobDescription
573 {
574 protected $valfield = 'profile_job.description';
575 protected $from = 'profile_job';
576 protected $idfield = 'profile_job.pid';
577
578 protected $ac_unique = 'profile_job.pid';
579 }
580 // }}}
581
582 /** NETWORKING
583 */
584 // {{{ class DE_Networking
585 class DE_Networking extends DirEnumeration
586 {
587 protected $idfield = 'profile_networking_enum.network_type';
588 protected $valfield = 'profile_networking_enum.name';
589 protected $from = 'profile_networking_enum';
590
591
592 protected $ac_join = 'INNER JOIN profile_networking ON (profile_networking.network_type = profile_networking_enum.network_type';
593 protected $ac_unique = 'profile_networking.uid';
594 }
595 // }}}
596 ?>