567f5e2277e90d59a6ec681dda8aedc51eb79e81
[platal.git] / include / profilefields.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 ProfileField
23 /** To store a "field" from the profile
24 * Provides functions for loading a batch of such data
25 */
26 abstract class ProfileField
27 {
28 /** The profile to which this field belongs
29 */
30 public $pid;
31
32 /** Fetches data from the database for the given pids, compatible with
33 * the visibility context.
34 * @param $pids An array of pids
35 * @param $visibility The level of visibility fetched fields must have
36 * @return a PlIterator yielding data suitable for a "new ProfileBlah($data)"
37 */
38 abstract public static function fetchData(array $pids, $visibility);
39
40 public static function buildForPID($cls, $pid, $visibility)
41 {
42 $res = self::buildFromPIDs($cls, array($pid), $visibility);
43 return array_pop($res);
44 }
45
46 /** Build a list of ProfileFields from a set of pids
47 * @param $cls The name of the field to create ('ProfileMedals', ...)
48 * @param $pids An array of pids
49 * @param $visibility An array of allowed visibility contexts
50 * @return An array of $pid => ProfileField
51 */
52 public static function buildFromPIDs($cls, array $pids, $visibility)
53 {
54 $it = new ProfileFieldIterator($cls, $pids, $visibility);
55 $res = array();
56 while ($pf = $it->next()) {
57 $res[$pf->pid] = $pf;
58 }
59 return $res;
60 }
61
62 public static function getForPID($cls, $pid, $visibility)
63 {
64 $it = new ProfileFieldIterator($cls, array($pid), $visibility);
65 return $it->next();
66 }
67 }
68 // }}}
69
70 // {{{ class ProfileFieldIterator
71 class ProfileFieldIterator implements PlIterator
72 {
73 private $data;
74 private $cls;
75
76 public function __construct($cls, array $pids, $visibility)
77 {
78 $this->data = call_user_func(array($cls, 'fetchData'), $pids, $visibility);
79 $this->cls = $cls;
80 }
81
82 public function next()
83 {
84 $d = $this->data->next();
85 if ($d == null) {
86 return null;
87 } else {
88 $cls = $this->cls;
89 return new $cls($d);
90 }
91 }
92
93 public function total()
94 {
95 return $this->data->total();
96 }
97
98 public function first()
99 {
100 return $this->data->first();
101 }
102
103 public function last()
104 {
105 return $this->data->last();
106 }
107 }
108 // }}}
109
110 // {{{ class Phone
111 class Phone
112 {
113 const TYPE_FAX = 'fax';
114 const TYPE_FIXED = 'fixed';
115 const TYPE_MOBILE = 'mobile';
116 public $type;
117
118 public $search;
119 public $display;
120 public $comment = '';
121
122 const LINK_JOB = 'job';
123 const LINK_ADDRESS = 'address';
124 const LINK_PROFILE = 'user';
125 const LINK_COMPANY = 'hq';
126 public $link_type;
127 public $link_id;
128
129 /** Fields are :
130 * $type, $search, $display, $link_type, $link_id, $comment, $pid, $id
131 */
132 public function __construct($data)
133 {
134 foreach ($data as $key => $val) {
135 $this->$key = $val;
136 }
137 }
138 }
139 // }}}
140 // {{{ class Company
141 class Company
142 {
143 public $id;
144 public $name;
145 public $acronym;
146 public $url;
147 public $phone = null;
148 public $address = null;
149
150 /** Fields are:
151 * $id, $name, $acronym, $url
152 */
153 public function __construct($data)
154 {
155 foreach ($data as $key => $val) {
156 $this->$key = $val;
157 }
158 }
159
160 public function setPhone(Phone &$phone)
161 {
162 if ($phone->link_type == Phone::LINK_COMPANY && $phone->link_id == $this->id) {
163 $this->phone = $phone;
164 }
165 }
166
167 public function setAddress(Address &$address)
168 {
169 if ($address->link_type == Address::LINK_COMPANY && $address->link_id == $this->id) {
170 $this->address = $address;
171 }
172 }
173
174 }
175 // }}}
176 // {{{ class Job
177 class Job
178 {
179 public $pid;
180 public $id;
181
182 public $company = null;
183 private $phones = array();
184 private $address = null;
185
186 public $jobid;
187
188 public $description;
189 public $user_site;
190 public $user_email;
191
192 public $sector;
193 public $subsector;
194 public $subsubsector;
195
196 /** Fields are:
197 * pid, id, company_id, description, url, email
198 */
199 public function __construct($data)
200 {
201 foreach ($data as $key => $val) {
202 $this->$key = $val;
203 }
204 $this->company = CompanyList::get($this->jobid);
205 }
206
207 public function phones()
208 {
209 return $this->phones;
210 }
211
212 public function addPhone(Phone &$phone)
213 {
214 if ($phone->link_type == Phone::LINK_JOB && $phone->link_id == $this->id && $phone->pid == $this->pid) {
215 $this->phones[] = $phone;
216 }
217 }
218
219 public function setAddress(Address $address)
220 {
221 if ($address->link_id == Address::LINK_JOB && $address->link_id == $this->id && $address->pid == $this->pid) {
222 $this->address = $address;
223 }
224 }
225 }
226 // }}}
227 // {{{ class Address
228 class Address
229 {
230 const LINK_JOB = 'job';
231 const LINK_COMPANY = 'hq';
232 const LINK_PROFILE = 'home';
233
234 public $flags;
235 public $link_id;
236 public $link_type;
237
238 public $text;
239 public $postalCode;
240 public $latitude;
241 public $longitude;
242
243 public $locality;
244 public $subAdministrativeArea;
245 public $administrativeArea;
246 public $country;
247
248 private $phones = array();
249
250 /** Fields are:
251 * pîd, id, link_id, link_type, flags, text, postcode, country
252 */
253 public function __construct($data)
254 {
255 foreach ($data as $key => $val) {
256 $this->$key = $val;
257 }
258 }
259
260 public function addPhone(Phone &$phone)
261 {
262 if ($phone->link_type == Phone::LINK_ADDRESS && $phone->link_id == $this->id && $phone->pid == $this->pid) {
263 $this->phones[] = $phone;
264 }
265 }
266
267 public function phones()
268 {
269 return $this->phones;
270 }
271
272 public function hasFlags($flags)
273 {
274 return $flags & $this->flags;
275 }
276 }
277 // }}}
278 // {{{ class Education
279 class Education
280 {
281 public $eduid;
282 public $degreeid;
283 public $fieldid;
284
285 public $entry_year;
286 public $grad_year;
287 public $program;
288 public $flags;
289
290 public function __construct(array $data)
291 {
292 $this->eduid = $data['eduid'];
293 $this->degreeid = $data['degreeid'];
294 $this->fieldid = $data['fieldid'];
295
296 $this->entry_year = $data['entry_year'];
297 $this->grad_year = $data['grad_year'];
298 $this->program = $data['program'];
299 $this->flags = new PlFlagSet($data['flags']);
300 }
301 }
302 // }}}
303
304 // {{{ class ProfileEducation [ Field ]
305 class ProfileEducation extends ProfileField
306 {
307 private $educations = array();
308
309 public function __construct(PlIterator $it)
310 {
311 $this->pid = $it->value();
312 $this->visibility = Profile::VISIBILITY_PUBLIC;
313 while ($edu = $it->next()) {
314 $this->educations[$edu['id']] = new Education($edu);
315 }
316 }
317
318 public function get($flags, $limit)
319 {
320 $educations = array();
321 $year = getdate();
322 $year = $year['year'];
323 $nb = 0;
324 foreach ($this->educations as $id => $edu) {
325 if (
326 (($flags & Profile::EDUCATION_MAIN) && $edu->flags->hasFlag('primary'))
327 ||
328 (($flags & Profile::EDUCATION_EXTRA) && !$edu->flags->hasFlag('primary'))
329 ||
330 (($flags & Profile::EDUCATION_FINISHED) && $edu->grad_year <= $year)
331 ||
332 (($flags & Profile::EDUCATION_CURRENT) && $edu->grad_year > $year)
333 ) {
334 $educations[$id] = $edu;
335 ++$nb;
336 }
337 if ($limit != null && $nb >= $limit) {
338 break;
339 }
340 }
341 return PlIteratorUtils::fromArray($educations);
342 }
343
344 public static function fetchData(array $pids, $visibility)
345 {
346 $data = XDB::iterator('SELECT id, pid, eduid, degreeid, fieldid,
347 entry_year, grad_year, program, flags
348 FROM profile_education
349 WHERE pid IN {?}
350 ORDER BY ' . XDB::formatCustomOrder('pid', $pids) . ',
351 NOT FIND_IN_SET(\'primary\', flags), entry_year, id',
352 $pids);
353
354 return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
355 }
356 }
357 // }}}
358 // {{{ class ProfileMedals [ Field ]
359 class ProfileMedals extends ProfileField
360 {
361 public $medals = array();
362
363 public function __construct(PlIterator $it)
364 {
365 while ($medal = $it->next()) {
366 $this->medals[$medal['mid']] = $medal['gid'];
367 }
368 }
369
370 public static function fetchData(array $pids, $visibility)
371 {
372 $data = XDB::iterator('SELECT pm.pid, pm.mid, pm.gid
373 FROM profile_medals AS pm
374 LEFT JOIN profiles AS p ON (pm.pid = p.pid)
375 WHERE pm.pid IN {?} AND p.medals_pub IN {?}
376 ORDER BY ' . XDB::formatCustomOrder('pm.pid', $pids),
377 XDB::formatArray($pids),
378 XDB::formatArray($visibility)
379 );
380
381 return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
382 }
383 }
384 // }}}
385 // {{{ class ProfileNetworking [ Field ]
386 class ProfileNetworking extends ProfileField
387 {
388 private $networks = array();
389
390 public function __construct(PlIterator $it)
391 {
392 while ($network = $it->next()) {
393 $this->networks[$network['nwid']] = $network['address'];
394 }
395 }
396
397 public static function fetchData(array $pids, $visibility)
398 {
399 $data = XDB::iterator('SELECT pid, nwid, address, network_type
400 FROM profile_networking
401 WHERE pid IN {?} AND pub IN {?}
402 ORDER BY ' . XDB::formatCustomOrder('pid', $pids) . ',
403 network_type, nwid',
404 $pids, $visibility);
405
406 return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
407 }
408
409 public function get($flags, $limit = null)
410 {
411 $nws = array();
412 $nb = 0;
413 foreach ($this->networks as $id => $nw) {
414 // XXX hardcoded reference to web site index
415 if (
416 (($flags & self::NETWORKING_WEB) && $nw['network_type'] == 0)
417 ||
418 (! ($flags & self::NETWORKING_WEB))
419 ) {
420 $nws[$id] = $nw;
421 ++$nb;
422 }
423 if ($nb >= $limit) {
424 break;
425 }
426 }
427 return PlIteratorUtils::fromArray($nws);
428 }
429 }
430 // }}}
431 // {{{ class ProfilePhoto [ Field ]
432 class ProfilePhoto extends ProfileField
433 {
434 public $pic;
435
436 public function __construct(array $data)
437 {
438 if ($data == null || count($data) == 0) {
439 $this->pic = null;
440 } else {
441 $this->pid = $data['pid'];
442 $this->pic = PlImage::fromDATA($data['attach'],
443 $data['attachmime'],
444 $data['x'],
445 $data['y']);
446 }
447 }
448
449 public static function fetchData(array $pids, $visibility)
450 {
451 $data = XDB::iterator('SELECT *
452 FROM profile_photos
453 WHERE pid IN {?} AND attachmime IN (\'jpeg\', \'png\') AND pub IN {?}
454 ORDER BY ' . XDB::formatCustomOrder('pid', $pids),
455 $pids, $visibility);
456
457 return $data;
458 }
459 }
460 // }}}
461 // {{{ class ProfileCorps [ Field ]
462 class ProfileCorps extends ProfileField
463 {
464 public $original;
465 public $current;
466 public $rank;
467
468 public function __construct(array $data)
469 {
470 $this->original = $data['original_corpsid'];
471 $this->current = $data['current_corpsid'];
472 $this->rank = $data['rankid'];
473 $this->visibility = $data['corps_pub'];
474 }
475
476 public static function fetchData(array $pids, $visibility)
477 {
478 $data = XDB::iterator('SELECT pid, original_corpsid, current_corpsid,
479 rankid
480 FROM profile_corps
481 WHERE pid IN {?} AND corps_pub IN {?}
482 ORDER BY ' . XDB::formatCustomOrder('pid', $pids),
483 XDB::formatArray($pids),
484 XDB::formatArray($visibility)
485 );
486
487 return $data;
488 }
489 }
490 // }}}
491
492 /** Loading of data for a Profile :
493 * 1) load jobs, addresses, phones
494 * 2) attach phones to addresses, jobs and profiles
495 * 3) attach addresses to jobs and profiles
496 */
497
498 // {{{ class ProfileAddresses [ Field ]
499 class ProfileAddresses extends ProfileField
500 {
501 private $addresses = array();
502
503 public function __construct(PlIterator $it)
504 {
505 if ($it instanceof PlInnerSubIterator) {
506 $this->pid = $it->value();
507 }
508
509 while ($addr = $it->next()) {
510 $this->addresses[] = new Address($addr);
511 }
512 }
513
514 public function get($flags, $limit = null)
515 {
516 $res = array();
517 $nb = 0;
518 foreach ($this->addresses as $addr) {
519 if ($addr->hasFlags($flags)) {
520 $res[] = $addr;
521 $nb++;
522 }
523 if ($limit != null && $nb == $limit) {
524 break;
525 }
526 }
527 return PlIteratorUtils::fromArray($res);
528 }
529
530 public static function fetchData(array $pids, $visibility)
531 {
532 $data = XDB::iterator('SELECT pa.id, pa.pid, pa.flags, pa.type AS link_type,
533 IF(pa.type = \'home\', pid, jobid) AS link_id,
534 pa.text, pa.postalCode, pa.latitude, pa.longitude,
535 gl.name AS locality, gas.name AS subAdministrativeArea,
536 ga.name AS administrativeArea, gc.countryFR AS country
537 FROM profile_addresses AS pa
538 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
539 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
540 LEFT JOIN geoloc_administrativeareas AS gas ON (gas.id = pa.subAdministrativeAreaId)
541 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
542 WHERE pa.pid in {?} AND pa.pub IN {?}
543 ORDER BY ' . XDB::formatCustomOrder('pid', $pids),
544 $pids, $visibility);
545
546 return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
547 }
548
549 public function addPhones(ProfilePhones $phones)
550 {
551 $p = $phones->get(0);
552 while ($phone = $p->next()) {
553 if ($phone->link_type == Phone::LINK_ADDRESS && array_key_exists($phone->link_id, $this->addresses)) {
554 $this->addresses[$phone->link_id]->addPhone($phone);
555 }
556 }
557 }
558 }
559 // }}}
560 // {{{ class ProfilePhones [ Field ]
561 class ProfilePhones extends ProfileField
562 {
563 private $phones = array();
564
565 public function __construct(PlIterator $phones)
566 {
567 while ($phone = $it->next()) {
568 $this->phones[] = Phone::buildFromData($phone);
569 }
570 }
571
572 public function get($flags, $limit = null)
573 {
574 $phones = array();
575 $nb = 0;
576 foreach ($this->phones as $id => $phone) {
577 $phones[$id] = $phone;
578 ++$nb;
579 if ($limit != null && $nb == $limit) {
580 break;
581 }
582 }
583 return PlIteratorUtils::fromArray($phones);
584 }
585
586 public static function fetchData(array $pids, $visibility)
587 {
588 $data = XDB::iterator('SELECT type, search, display, link_type, comment
589 FROM profile_phones
590 WHERE pid IN {?} AND pub IN {?}
591 ORDER BY ' . XDB::formatCustomOrder('pid', $pids),
592 XDB::formatArray($pids),
593 XDB::formatArray($visibility)
594 );
595 return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
596 }
597 }
598 // }}}
599 // {{{ class ProfileJobs [ Field ]
600 class ProfileJobs extends ProfileField
601 {
602 private $jobs = array();
603
604 public function __construct(PlIterator $jobs)
605 {
606 while ($job = $jobs->next()) {
607 $this->jobs[$job['id']] = new Job($job);
608 }
609 }
610
611 public static function fetchData(array $pids, $visibility)
612 {
613 CompanyList::preload($pids);
614 $data = XDB::iterator('SELECT pj.id, pj.pid, pj.description, pj.url as user_site,
615 IF(pj.email_pub IN {?}, pj.email, NULL) AS user_email,
616 pj.jobid, pjse.name AS sector, pjsse.name AS subsector,
617 pjssse.name AS subsubsector
618 FROM profile_job AS pj
619 LEFT JOIN profile_job_sector_enum AS pjse ON (pjse.id = pj.sectorid)
620 LEFT JOIN profile_job_subsector_enum AS pjsse ON (pjsse.id = pj.subsectorid)
621 LEFT JOIN profile_job_subsubsector_enum AS pjssse ON (pjssse.id = pj.subsubsectorid)
622 WHERE pj.pid IN {?} AND pj.pub IN {?}
623 ORDER BY ' . XDB::formatCustomOrder('pid', $pids) . ',
624 pj.id',
625 $visibility, $pids, $visibility);
626 return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
627 }
628
629 public function get($flags, $limit = null)
630 {
631 $jobs = array();
632 $nb = 0;
633 foreach ($this->jobs as $id => $job) {
634 $jobs[$id] = $job;
635 ++$nb;
636 if ($limit != null && $nb >= $limit) {
637 break;
638 }
639 }
640 return PlIteratorUtils::fromArray($jobs);
641 }
642
643 public function addPhones(ProfilePhones $phones)
644 {
645 $p = $phones->get(0);
646 while ($phone = $p->next()) {
647 if ($phone->link_type == Phone::LINK_JOB && array_key_exists($phone->link_id, $this->jobs)) {
648 $this->jobs[$phone->link_id]->addPhones($phone);
649 }
650 }
651 }
652
653 public static function addAddresses(ProfileAddresses $addresses)
654 {
655 $a = $addresses->get(Profile::ADDRESS_PRO);
656 while ($address = $a->next()) {
657 if ($address->link_type == Address::LINK_JOB && array_key_exists($address->link_id, $this->jobs)) {
658 $this->jobs[$address->link_id]->setAddress($address);
659 }
660 }
661 }
662
663 public static function addCompanies(array $companies)
664 {
665 foreach ($this->jobs as $job)
666 {
667 $job->company = $companies[$job->jobid];
668 }
669 }
670 }
671 // }}}
672
673 // {{{ class CompanyList
674 class CompanyList
675 {
676 static private $fullload = false;
677 static private $companies = array();
678
679 static public function preload($pids = array())
680 {
681 if (self::$fullload) {
682 return;
683 }
684 // Load raw data
685 if (count($pids)) {
686 $join = 'LEFT JOIN profile_job ON (profile_job.jobid = pje.id)';
687 $where = 'WHERE profile_job.pid IN ' . XDB::formatArray($pids);
688 } else {
689 $join = '';
690 $where = '';
691 }
692
693 $it = XDB::iterator('SELECT pje.id, pje.name, pje.acronym, pje.url,
694 pa.flags, pa.text, pa.postalCode, pa.countryId,
695 pa.type, pa.pub
696 FROM profile_job_enum AS pje
697 LEFT JOIN profile_addresses AS pa ON (pje.id = pa.jobid AND pa.type = \'hq\')
698 ' . $join . '
699 ' . $where);
700 while ($row = $it->next()) {
701 $cp = new Company($row);
702 $addr = new Address($row);
703 $cp->setAddress($addr);
704 self::$companies[$row['id']] = $cp;
705 }
706
707 // TODO: add phones to addresses
708 if (count($pids) == 0) {
709 self::$fullload = true;
710 }
711 }
712
713 static public function get($id)
714 {
715 if (!array_key_exists($id, self::$companies)) {
716 self::preload();
717 }
718 return self::$companies[$id];
719 }
720 }
721
722 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
723 ?>