Fix.
[platal.git] / classes / profile.php
CommitLineData
e7b93962
FB
1<?php
2/***************************************************************************
d4c08d89 3 * Copyright (C) 2003-2010 Polytechnique.org *
e7b93962
FB
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
22class Profile
23{
f5642983
FB
24 static private $v_values = array('public' => array('public'),
25 'ax' => array('ax', 'public'),
26 'private' => array('private', 'ax', 'public'));
27 const VISIBILITY_PUBLIC = 'public';
28 const VISIBILITY_AX = 'ax';
29 const VISIBILITY_PRIVATE = 'private';
30
31 const ADDRESS_MAIN = 0x000001;
32 const ADDRESS_PERSO = 0x000002;
33 const ADDRESS_PRO = 0x000004;
34 const ADDRESS_ALL = 0x000006;
35 const ADDRESS_POSTAL = 0x000008;
36
37 const EDUCATION_MAIN = 0x000010;
4bc5b8f0
FB
38 const EDUCATION_EXTRA = 0x000020;
39 const EDUCATION_ALL = 0x000040;
40 const EDUCATION_FINISHED = 0x000080;
41 const EDUCATION_CURRENT = 0x000100;
f5642983 42
4bc5b8f0
FB
43 const JOBS_MAIN = 0x001000;
44 const JOBS_ALL = 0x002000;
45 const JOBS_FINISHED = 0x004000;
46 const JOBS_CURRENT = 0x008000;
f5642983 47
04a94b1d
FB
48 const NETWORKING_ALL = 0x000000;
49 const NETWORKING_WEB = 0x010000;
50 const NETWORKING_IM = 0x020000;
51 const NETWORKING_SOCIAL = 0x040000;
52
e7b93962
FB
53 private $pid;
54 private $hrpid;
3e53a496
FB
55 private $data = array();
56
f5642983
FB
57 private $visibility = null;
58
b774ddab 59 private function __construct(array $data)
e7b93962 60 {
b774ddab 61 $this->data = $data;
832e6fcb
FB
62 $this->pid = $this->data['pid'];
63 $this->hrpid = $this->data['hrpid'];
f74fb084
FB
64 if (!S::logged()) {
65 $this->setVisibilityLevel(self::VISIBILITY_PUBLIC);
66 }
e7b93962
FB
67 }
68
69 public function id()
70 {
71 return $this->pid;
72 }
73
74 public function hrid()
75 {
76 return $this->hrpid;
77 }
78
d5e60905
FB
79 public function promo()
80 {
81 return $this->promo;
82 }
83
94b72319
FB
84 /** Print a name with the given formatting:
85 * %s = • for women
86 * %f = firstname
87 * %l = lastname
88 * %F = fullname
89 * %S = shortname
90 * %p = promo
91 */
92 public function name($format)
93 {
94 return str_replace(array('%s', '%f', '%l', '%F', '%S', '%p'),
95 array($this->isFemale() ? '•' : '',
96 $this->first_name, $this->last_name,
97 $this->full_name, $this->short_name,
98 $this->promo), $format);
99 }
100
101 public function fullName($with_promo = false)
102 {
103 if ($with_promo) {
104 return $this->full_name . ' (' . $this->promo . ')';
105 }
106 return $this->full_name;
107 }
108
109 public function shortName($with_promo = false)
110 {
111 if ($with_promo) {
112 return $this->short_name . ' (' . $this->promo . ')';
113 }
114 return $this->short_name;
115 }
116
117 public function firstName()
118 {
08c91036 119 return $this->firstname;
94b72319
FB
120 }
121
122 public function lastName()
123 {
08c91036 124 return $this->lastname;
94b72319
FB
125 }
126
127 public function isFemale()
128 {
129 return $this->sex == PlUser::GENDER_FEMALE;
130 }
131
132 public function data()
133 {
134 $this->first_name;
135 return $this->data;
136 }
137
3e53a496
FB
138 public function __get($name)
139 {
140 if (property_exists($this, $name)) {
141 return $this->$name;
142 }
143
3e53a496
FB
144 if (isset($this->data[$name])) {
145 return $this->data[$name];
146 }
147
148 return null;
149 }
150
151 public function __isset($name)
152 {
153 return property_exists($this, $name) || isset($this->data[$name]);
154 }
155
f5642983
FB
156 public function setVisibilityLevel($visibility)
157 {
158 if ($visibility != self::VISIBILITY_PRIVATE
159 && $visibility != self::VISIBILITY_AX
160 && $visibility != self::VISIBILITY_PUBLIC) {
161 Platal::page()->kill("Visibility invalide: " . $visibility);
162 }
163 $this->visibility = self::$v_values[$visibility];
46e9eb99
FB
164 if ($this->mobile && !in_array($this->modbile_pub, $this->visibility)) {
165 unset($this->data['mobile']);
166 }
f5642983
FB
167 }
168
4bc5b8f0 169
833a6e86
FB
170 /* Photo
171 */
172 public function getPhoto($fallback = true)
173 {
174 /* TODO: migrate photo table to profile_photo, change uid to pid
175 */
176 $cond = '';
177 if ($this->visibility) {
178 $cond = ' AND pub IN ' . XDB::formatArray($this->visibility);
179 }
180 $res = XDB::query('SELECT *
181 FROM photo
182 WHERE attachmime IN (\'jpeg\', \'png\')
183 ' . $cond . ' AND uid = {?}',
184 $this->id());
185 if ($res->numRows() > 0) {
186 $photo = $res->fetchOneAssoc();
187 return PlImage::fromData($photo['attach'], 'image/' . $photo['attachmime'],
188 $photo['x'], $photo['y']);
189 } else if ($fallback) {
190 return PlImage::fromFile(dirname(__FILE__).'/../htdocs/images/none.png',
191 'image/png');
192 }
193 return null;
194 }
195
4bc5b8f0
FB
196 /* Addresses
197 */
198 public function getAddresses($flags, $limit = null)
f5642983
FB
199 {
200 $where = XDB::format('pa.pid = {?}', $this->id());
201 if ($flags & self::ADDRESS_MAIN) {
202 $where .= ' AND FIND_IN_SET(\'current\', pa.flags)';
203 }
204 if ($flags & self::ADDRESS_POSTAL) {
205 $where .= ' AND FIND_IN_SET(\'mail\', pa.flags)';
206 }
207 if ($this->visibility) {
208 $where .= ' AND pa.pub IN ' . XDB::formatArray($this->visibility);
209 }
210 $type = array();
211 if ($flags & self::ADDRESS_PRO) {
212 $type[] = 'job';
213 }
214 if ($flags & self::ADDRESS_PERSO) {
215 $type[] = 'home';
216 }
217 if (count($type) > 0) {
218 $where .= ' AND pa.type IN ' . XDB::formatArray($type);
219 }
4bc5b8f0 220 $limit = is_null($limit) ? '' : XDB::format('LIMIT {?}', (int)$limit);
f5642983
FB
221 return XDB::iterator('SELECT pa.text, pa.postalCode, pa.type, pa.latitude, pa.longitude,
222 gl.name AS locality, gas.name AS subAdministrativeArea,
223 ga.name AS administrativeArea, gc.countryFR AS country,
224 FIND_IN_SET(\'current\', pa.flags) AS current,
225 FIND_IN_SET(\'temporary\', pa.flags) AS temporary,
226 FIND_IN_SET(\'secondary\', pa.flags) AS secondary,
227 FIND_IN_SET(\'mail\', pa.flags) AS mail, pa.type
228 FROM profile_addresses AS pa
229 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
230 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
231 LEFT JOIN geoloc_administrativeareas AS gas ON (gas.id = pa.subAdministrativeAreaId)
232 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
4bc5b8f0
FB
233 WHERE ' . $where . '
234 ORDER BY pa.id
235 ' . $limit);
f5642983
FB
236 }
237
238 public function getMainAddress()
239 {
240 $it = $this->getAddresses(self::ADDRESS_PERSO | self::ADDRESS_MAIN);
241 if ($it->total() == 0) {
242 return null;
243 } else {
244 return $it->next();
245 }
246 }
3e53a496 247
4bc5b8f0
FB
248
249 /* Educations
250 */
251 public function getEducations($flags, $limit = null)
252 {
253 $where = XDB::format('pe.uid = {?}', $this->id());
254 if ($flags & self::EDUCATION_MAIN) {
255 $where .= ' AND FIND_IN_SET(\'primary\', pe.flags)';
256 } else if ($flags & self::EDUCATION_EXTRA) {
257 $where .= ' AND NOT FIND_IN_SET(\'primary\', pe.flags)';
258 } else if ($flags & self::EDUCATION_FINISHED) {
259 $where .= ' AND pe.grad_year <= YEAR(CURDATE())';
260 } else if ($flags & self::EDUCATION_CURRENT) {
261 $where .= ' AND pe.grad_year > YEAR(CURDATE())';
262 }
263 $limit = is_null($limit) ? '' : XDB::format('LIMIT {?}', (int)$limit);
264 return XDB::iterator('SELECT pe.entry_year, pe.grad_year, pe.program,
265 pee.name AS school, pee.abbreviation AS school_short, pee.url AS school_url, gc.countryFR AS country,
266 pede.degree, pede.abbreviation AS degree_short, pede.level AS degree_level, pefe.field,
267 FIND_IN_SET(\'primary\', pe.flags) AS prim
268 FROM profile_education AS pe
269 INNER JOIN profile_education_enum AS pee ON (pe.eduid = pee.id)
270 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pee.country)
271 INNER JOIN profile_education_degree_enum AS pede ON (pe.degreeid = pede.id)
272 LEFT JOIN profile_education_field_enum AS pefe ON (pe.fieldid = pefe.id)
273 WHERE ' . $where . '
274 ORDER BY NOT FIND_IN_SET(\'primary\', pe.flags), pe.entry_year, pe.id
275 ' . $limit);
276 }
277
278 public function getExtraEducations($limit = null)
279 {
280 return $this->getEducations(self::EDUCATION_EXTRA, $limit);
281 }
282
283
04a94b1d
FB
284 /** Networking
285 */
286
287 public function getNetworking($flags, $limit = null)
288 {
289 $where = XDB::format('pn.uid = {?}', $this->id());
290 if ($flags & self::NETWORKING_WEB) {
291 $where .= ' AND pn.network_type = 0'; // XXX hardcoded reference to web site index
292 }
293 if ($this->visibility) {
294 $where .= ' AND pn.pub IN ' . XDB::formatArray($this->visibility);
295 }
296 $limit = is_null($limit) ? '' : XDB::format('LIMIT {?}', (int)$limit);
297 return XDB::iterator('SELECT pne.name, pne.icon,
298 IF (LENGTH(pne.link) > 0, REPLACE(pne.link, \'%s\', pn.address),
299 pn.address) AS address
300 FROM profile_networking AS pn
301 INNER JOIN profile_networking_enum AS pne ON (pn.network_type = pne.network_type)
302 WHERE ' . $where . '
303 ORDER BY pn.network_type, pn.nwid
304 ' . $limit);
305 }
306
307 public function getWebSite()
308 {
309 $site = $this->getNetworking(self::NETWORKING_WEB, 1);
310 if ($site->total() != 1) {
311 return null;
312 }
313 $site = $site->next();
314 return $site['address'];
315 }
316
317
e718bd18
FB
318 /** Jobs
319 */
320
321 public function getJobs($flags, $limit = null)
322 {
323 $where = XDB::format('pj.uid = {?}', $this->id());
324 $cond = 'TRUE';
325 if ($this->visibility) {
326 $where .= ' AND pj.pub IN ' . XDB::formatArray($this->visibility);
327 $cond = 'pj.email_pub IN ' . XDB::formatArray($this->visibility);
328 }
329 $limit = is_null($limit) ? '' : XDB::format('LIMIT {?}', (int)$limit);
330 return XDB::iterator('SELECT pje.name, pje.acronym, pje.url, pje.email, pje.NAF_code,
331 pj.description, pj.url AS user_site,
332 IF (' . $cond . ', pj.email, NULL) AS user_email,
83ff5281
SJ
333 pjse.name AS sector, pjsse.name AS subsector,
334 pjssse.name AS subsubsector
e718bd18
FB
335 FROM profile_job AS pj
336 INNER JOIN profile_job_enum AS pje ON (pje.id = pj.jobid)
e718bd18
FB
337 LEFT JOIN profile_job_sector_enum AS pjse ON (pjse.id = pj.sectorid)
338 LEFT JOIN profile_job_subsector_enum AS pjsse ON (pjsse.id = pj.subsectorid)
339 LEFT JOIN profile_job_subsubsector_enum AS pjssse ON (pjssse.id = pj.subsubsectorid)
340 WHERE ' . $where . '
341 ORDER BY pj.id
342 ' . $limit);
343 }
344
345 public function getMailJob()
346 {
347 $job = $this->getJobs(self::JOBS_MAIN, 1);
348 if ($job->total() != 1) {
349 return null;
350 }
351 return $job->next();
352 }
353
354
e7b93962
FB
355 public function owner()
356 {
357 return User::getSilent($this);
358 }
359
b774ddab
FB
360 private static function fetchProfileData(array $pids)
361 {
362 if (count($pids) == 0) {
363 return array();
364 }
365 return XDB::fetchAllAssoc('SELECT p.*, p.sex = \'female\' AS sex, pe.entry_year, pe.grad_year,
366 pn_f.name AS firstname, pn_l.name AS lastname, pn_n.name AS nickname,
367 IF(pn_uf.name IS NULL, pn_f.name, pn_uf.name) AS firstname_usual,
368 IF(pn_ul.name IS NULL, pn_l.name, pn_ul.name) AS lastname_usual,
46e9eb99 369 pd.promo AS promo, pd.short_name, pd.directory_name AS full_name,
833a6e86 370 pp.display_tel AS mobile, pp.pub AS mobile_pub, ph.pub AS photo_pub
b774ddab
FB
371 FROM profiles AS p
372 INNER JOIN profile_display AS pd ON (pd.pid = p.pid)
373 INNER JOIN profile_education AS pe ON (pe.uid = p.pid AND FIND_IN_SET(\'primary\', pe.flags))
374 INNER JOIN profile_name AS pn_f ON (pn_f.pid = p.pid
767025be 375 AND pn_f.typeid = ' . self::getNameTypeId('firstname', true) . ')
b774ddab 376 INNER JOIN profile_name AS pn_l ON (pn_l.pid = p.pid
767025be 377 AND pn_l.typeid = ' . self::getNameTypeId('lastname', true) . ')
b774ddab 378 LEFT JOIN profile_name AS pn_uf ON (pn_uf.pid = p.pid
767025be 379 AND pn_uf.typeid = ' . self::getNameTypeId('firstname_ordinary', true) . ')
b774ddab 380 LEFT JOIN profile_name AS pn_ul ON (pn_ul.pid = p.pid
767025be 381 AND pn_ul.typeid = ' . self::getNameTypeId('lastname_ordinary', true) . ')
46e9eb99 382 LEFT JOIN profile_name AS pn_n ON (pn_n.pid = p.pid
b774ddab 383 AND pn_n.typeid = ' . self::getNameTypeId('nickname', true) . ')
46e9eb99 384 LEFT JOIN profile_phones AS pp ON (pp.uid = p.pid AND pp.link_type = \'user\' AND tel_type = \'mobile\')
833a6e86 385 LEFT JOIN photo AS ph ON (ph.uid = p.pid)
b774ddab
FB
386 WHERE p.pid IN ' . XDB::formatArray($pids) . '
387 GROUP BY p.pid');
388 }
389
390 public static function getPID($login)
391 {
392 if ($login instanceof PlUser) {
393 return XDB::fetchOneCell('SELECT pid
394 FROM account_profiles
395 WHERE uid = {?} AND FIND_IN_SET(\'owner\', perms)',
396 $login->id());
397 } else if (ctype_digit($login)) {
398 return XDB::fetchOneCell('SELECT pid
399 FROM profiles
400 WHERE pid = {?}', $login);
401 } else {
402 return XDB::fetchOneCell('SELECT pid
403 FROM profiles
404 WHERE hrpid = {?}', $login);
405 }
406 }
407
408
e7b93962
FB
409 /** Return the profile associated with the given login.
410 */
a3118782
FB
411 public static function get($login)
412 {
b774ddab
FB
413 $pid = self::getPID($login);
414 if (!is_null($pid)) {
415 $data = self::fetchProfileData(array($pid));
416 return new Profile(array_pop($data));
417 } else {
efe597c5
FB
418 /* Let say we can identify a profile using the identifiers of its owner.
419 */
455ea0c9
FB
420 if (!($login instanceof PlUser)) {
421 $user = User::getSilent($login);
422 if ($user && $user->hasProfile()) {
423 return $user->profile();
424 }
efe597c5 425 }
3e53a496 426 return null;
e7b93962
FB
427 }
428 }
a3118782 429
b774ddab
FB
430 /** Return profiles for the list of pids.
431 */
432 public static function getBulkProfilesWithPIDs(array $pids)
433 {
434 if (count($pids) == 0) {
435 return array();
436 }
437 $data = self::fetchProfileData($pids);
438 $inv = array_flip($pids);
439 $profiles = array();
440 foreach ($data AS $p) {
441 $p = new Profile($p);
442 $key = $inv[$p->id()];
443 $profiles[$key] = $p;
444 }
445 return $profiles;
446 }
447
448 /** Return profiles for uids.
449 */
450 public static function getBulkProfilesWithUIDS(array $uids)
451 {
452 if (count($uids) == 0) {
453 return array();
454 }
455 $table = XDB::fetchAllAssoc('uid', 'SELECT ap.uid, ap.pid
456 FROM account_profiles AS ap
457 WHERE FIND_IN_SET(\'owner\', ap.perms)
458 AND ap.uid IN ' . XDB::formatArray($uids));
459 return self::getBulkProfilesWithPIDs($table);
460 }
461
a3118782
FB
462 public static function getNameTypeId($type, $for_sql = false)
463 {
464 if (!S::has('name_types')) {
eb6207f7 465 $table = XDB::fetchAllAssoc('type', 'SELECT id, type
32742f84 466 FROM profile_name_enum');
a3118782
FB
467 S::set('name_types', $table);
468 } else {
469 $table = S::v('name_types');
470 }
471 if ($for_sql) {
472 return XDB::escape($table[$type]);
473 } else {
474 return $table[$type];
475 }
476 }
e7b93962
FB
477}
478
479// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
480?>