2 /***************************************************************************
3 * Copyright (C) 2003-2014 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
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. *
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. *
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 *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
23 class WSDirectoryRequest
25 // Default number of returned results.
26 const DEFAULT_AMOUNT
= 20;
30 public $order = array();
32 protected $partner = null
;
34 const ORDER_RAND
= 'rand';
35 const ORDER_NAME
= 'name';
36 const ORDER_PROMOTION
= 'promotion';
38 public static $order_choices = array(
41 self
::ORDER_PROMOTION
,
44 public function __construct($partner, PlDict
$payload)
46 $this->partner
= $partner;
49 $this->fields
= array_intersect($payload->v('fields'), WSRequestFields
::$choices);
50 $this->order
= array_intersect($payload->v('order', array()), self
::$order_choices);
52 $this->criteria
= array();
53 $criteria = new PlDict($payload->v('criteria'));
54 foreach (WSRequestCriteria
::$choices_simple as $criterion) {
55 if ($criteria->has($criterion)) {
56 $this->criteria
[$criterion] = $criteria->s($criterion);
59 foreach (WSRequestCriteria
::$choices_enum as $criterion) {
60 if ($criteria->has($criterion)) {
61 $this->criteria
[$criterion] = $criteria->s($criterion);
64 foreach (WSRequestCriteria
::$choices_list as $criterion) {
65 if ($criteria->has($criterion)) {
66 $this->criteria
[$criterion] = $criteria->v($criterion);
70 // Amount may not exceed $globals->sharingapi->max_result_per_query.
71 $amount = $payload->i('amount', self
::DEFAULT_AMOUNT
);
72 $this->amount
= min($amount, $globals->sharingapi
->max_result_per_query
);
77 $cond = $this->getCond();
78 $cond->addChild(new UFC_PartnerSharing($this->partner
->id
));
79 $pf = new ProfileFilter($cond, $this->getOrders());
80 $pf->restrictVisibilityForPartner($this->partner
->id
);
82 $matches = $pf->getTotalProfileCount();
83 $response['matches'] = $matches;
87 // TODO : improve fetching by passing an adequate FETCH field
88 $iter = $pf->iterProfiles(new PlLimit($this->amount
), 0x0000, Visibility
::get(Visibility
::VIEW_PRIVATE
));
89 while ($profile = $iter->next()) {
90 if ($profile->getPartnerSettings($this->partner
->id
)->exposed_uid
!== 0) {
91 $profile_data = new WSRequestEntry($this->partner
, $profile);
92 $profiles[] = $profile_data->getFields($this->fields
);
96 $response['profiles'] = $profiles;
100 public function assignToPage(PlPage
$page)
102 $response = $this->get();
103 $page->jsonAssign('matches', $response['matches']);
104 $page->jsonAssign('profiles', $response['profiles']);
107 /** Compute the orders to use for the current request.
108 * @return array of PlFilterOrder
110 protected function getOrders()
113 foreach ($this->order
as $order)
116 case self
::ORDER_RAND
:
117 $orders[] = new PFO_Random();
119 case self
::ORDER_NAME
:
120 $orders[] = new UFO_Name(Profile
::DN_SORT
);
122 case self
::ORDER_PROMOTION
:
123 $orders[] = new UFO_Promo();
132 /** Compute the conditions to use for the current request.
133 * @return A PlFilterCondition instance (actually a PFC_And)
135 protected function getCond()
137 $cond = new PFC_And();
138 foreach ($this->criteria
as $criterion => $value) {
139 switch ($criterion) {
142 case WSRequestCriteria
::SCHOOL
:
143 // Useless criterion: we don't need to check on origin school
144 if (WSRequestCriteria
::$choices_enum[$criterion][$value]) {
145 $cond->addChild(new PFC_True());
147 $cond->addChild(new PFC_False());
150 case WSRequestCriteria
::DIPLOMA
:
151 $diploma = WSRequestCriteria
::$choices_enum[$criterion][$value];
152 $id_X = XDB
::fetchOneCell('SELECT id
153 FROM profile_education_enum
154 WHERE abbreviation = {?}', 'X');
155 $cond->addChildren(array(
156 new UFC_EducationSchool($id_X),
157 new UFC_EducationDegree($diploma),
162 case WSRequestCriteria
::FIRSTNAME
:
163 case WSRequestCriteria
::LASTNAME
:
164 $cond->addChild(new UFC_NameTokens($value, UFC_NameTokens
::FLAG_PUBLIC
, false
, false
, $criterion));
166 case WSRequestCriteria
::PROMOTION
:
167 $cond->addChild(new PFC_Or(
168 new UFC_Promo(UserFilter
::OP_EQUALS
,
169 UserFilter
::GRADE_ING
,
171 new UFC_Promo(UserFilter
::OP_EQUALS
,
172 UserFilter
::GRADE_MST
,
174 new UFC_Promo(UserFilter
::OP_EQUALS
,
175 UserFilter
::GRADE_PHD
,
179 case WSRequestCriteria
::ALT_DIPLOMA
:
181 new UFC_EducationDegree(
182 DirEnum
::getIds(DirEnum
::EDUDEGREES
, $value)));
184 case WSRequestCriteria
::DIPLOMA_FIELD
:
186 new UFC_EducationField(
187 DirEnum
::getIds(DirEnum
::EDUFIELDS
, $value)));
189 case WSRequestCriteria
::CITY
:
191 new UFC_AddressField($value,
192 UFC_AddressField
::FIELD_LOCALITY
,
193 UFC_Address
::TYPE_HOME
,
194 UFC_Address
::FLAG_CURRENT
));
196 case WSRequestCriteria
::COUNTRY
:
198 new UFC_AddressField($value,
199 UFC_AddressField
::FIELD_COUNTRY
,
200 UFC_Address
::TYPE_HOME
,
201 UFC_Address
::FLAG_CURRENT
));
203 case WSRequestCriteria
::ZIPCODE
:
205 new UFC_AddressField($value,
206 UFC_AddressField
::FIELD_ZIPCODE
,
207 UFC_Address
::TYPE_HOME
,
208 UFC_Address
::FLAG_CURRENT
));
210 case WSRequestCriteria
::JOB_ANY_COUNTRY
:
212 new UFC_AddressField($value,
213 UFC_AddressField
::FIELD_COUNTRY
,
214 UFC_Address
::TYPE_PRO
,
215 UFC_Address
::FLAG_ANY
));
217 case WSRequestCriteria
::JOB_CURRENT_CITY
:
219 new UFC_AddressField($value,
220 UFC_AddressField
::FIELD_LOCALITY
,
221 UFC_Address
::TYPE_PRO
,
222 UFC_Address
::FLAG_ANY
));
224 case WSRequestCriteria
::JOB_ANY_COMPANY
:
225 case WSRequestCriteria
::JOB_CURRENT_COMPANY
:
227 new UFC_Job_Company(UFC_Job_Company
::JOBNAME
,
230 case WSRequestCriteria
::JOB_ANY_SECTOR
:
231 case WSRequestCriteria
::JOB_CURRENT_SECTOR
:
232 case WSRequestCriteria
::JOB_CURRENT_TITLE
:
234 new UFC_Job_Terms(DirEnum
::getIds(DirEnum
::JOBTERMS
, $value)));
238 case WSRequestCriteria
::HOBBIES
:
239 $subcond = new PFC_Or();
240 foreach ($value as $val) {
241 $subcond->addChild(new UFC_Comment($value));
243 $cond->addChild($subcond);
245 case WSRequestCriteria
::JOB_COMPETENCIES
:
246 case WSRequestCriteria
::JOB_RESUME
:
247 case WSRequestCriteria
::PROFESSIONAL_PROJECT
:
248 $subcond = new PFC_Or();
249 foreach ($value as $val) {
251 new UFC_Job_Description($value, UserFilter
::JOB_USERDEFINED
));
253 $cond->addChild($subcond);
255 case WSRequestCriteria
::NOT_UID
:
258 new UFC_PartnerSharingID($this->partner
->id
, $value)));
270 const ERROR_MISSING_FIELDS
= 'missing_fields';
271 const ERROR_MISSING_CRITERIA
= 'missing_criteria';
272 const ERROR_MALFORMED_AMOUNT
= 'malformed_amount';
273 const ERROR_MALFORMED_ORDER
= 'malformed_order';
275 public static $ERROR_MESSAGES = array(
276 self
::ERROR_MISSING_FIELDS
=> "The 'fields' field is mandatory.",
277 self
::ERROR_MISSING_CRITERIA
=> "The 'criteria' field is mandatory.",
278 self
::ERROR_MALFORMED_AMOUNT
=> "The 'amount' value is invalid (expected an int)",
279 self
::ERROR_MALFORMED_ORDER
=> "The 'order' value is invalid (expected an array)",
282 /** Static method performing all input validation on the payload.
283 * @param PlDict $payload The payload to validate
284 * @return array Errors discovered when validating input
286 public static function validatePayload(PlDict
$payload)
289 if (!$payload->has('fields')) {
290 $errors[] = self
::ERROR_MISSING_FIELDS
;
292 if (!$payload->has('criteria')) {
293 $errors[] = self
::ERROR_MISSING_CRITERIA
;
296 if ($payload->has('amount') && $payload->i('amount', -1) < 0) {
297 $errors[] = self
::ERROR_MALFORMED_AMOUNT
;
300 if (!is_array($payload->v('order', array()))) {
301 $errors[] = self
::ERROR_MALFORMED_ORDER
;
308 // {{{ WSRequestEntry
309 /** Performs field retrieval for a profile.
313 private $profile = null
;
314 private $partner = null
;
315 private $settings = null
;
317 public function __construct($partner, $profile)
319 $this->partner
= $partner;
320 $this->profile
= $profile;
321 $this->settings
= $this->profile
->getPartnerSettings($this->partner
->id
);
324 public function isVisible($level)
326 return $this->settings
->sharing_visibility
->isVisible($level);
329 public function getFields($fields)
332 foreach ($fields as $field)
334 $val = $this->getFieldValue($field);
336 $data[$field] = $val;
339 $data['uid'] = $this->settings
->exposed_uid
;
343 protected function getFieldValue($field)
349 case WSRequestFields
::UID
:
350 // UID is always included
352 case WSRequestFields
::BIRTHDATE
:
353 case WSRequestFields
::FAMILY_POSITION
:
354 case WSRequestFields
::HONORARY_TITLES
:
355 case WSRequestFields
::LANGS
:
356 case WSRequestFields
::JOB_COMPETENCIES
:
357 case WSRequestFields
::RESUME
:
358 case WSRequestFields
::PROFESSIONAL_PROJECT
:
359 case WSRequestFields
::HOBBIES
:
364 case WSRequestFields
::FIRSTNAME
:
365 return $p->firstName();
366 case WSRequestFields
::LASTNAME
:
367 return $p->lastName();
368 case WSRequestFields
::GENDER
:
369 if ($p->isFemale()) {
370 return WSRequestFields
::GENDER_WOMAN
;
372 return WSRequestFields
::GENDER_MAN
;
374 case WSRequestFields
::SCHOOL
:
375 return WSRequestCriteria
::SCHOOL_X
;
376 case WSRequestFields
::DIPLOMA
:
377 $edu = $p->getEducations(Profile
::EDUCATION_MAIN
);
379 return WSRequestFields
::profileDegreeToWSDiploma(
380 array_pop($edu)->degree
);
384 case WSRequestFields
::DIPLOMA_FIELD
:
385 $edu = $p->getEducations(Profile
::EDUCATION_MAIN
);
387 return array_pop($edu)->field
;
391 case WSRequestFields
::PROMOTION
:
392 return $p->yearpromo();
393 case WSRequestFields
::ALT_DIPLOMAS
:
395 foreach ($p->getExtraEducations() as $edu) {
396 $diplomas[] = WSRequestFields
::profileDegreeToWSDiploma(
401 // Other generic profile fields
402 case WSRequestFields
::EMAIL
:
403 if ($this->settings
->sharing_visibility
->isVisible(Visibility
::EXPORT_PRIVATE
)) {
404 // If sharing "all" data, share best email.
405 return $p->displayEmail();
406 } elseif ($this->settings
->sharing_visibility
->isVisible(Visibility
::EXPORT_AX
)) {
407 // If sharing "AX" level, share "AX" email.
408 return $p->email_directory
;
410 // Otherwise, don't share.
413 case WSRequestFields
::MOBILE_PHONE
:
414 $phones = $p->getPhones(Profile
::PHONE_TYPE_MOBILE | Profile
::PHONE_LINK_PROFILE
);
415 if (count($phones)) {
416 $phone = array_pop($phones);
417 if ($this->isVisible($phone->pub
)) {
418 return $phone->display
;
422 case WSRequestFields
::PIC_SMALL
:
423 case WSRequestFields
::PIC_MEDIUM
:
424 case WSRequestFields
::PIC_LARGE
:
425 if ($this->isVisible($p->photo_pub
)) {
426 $token = sha1(uniqid(rand(), true
));
427 XDB
::execute('DELETE FROM profile_photo_tokens
428 WHERE pid = {?}', $p->pid
);
429 XDB
::execute('INSERT INTO profile_photo_tokens
430 SET pid = {?}, token = {?},
431 expires = ADDTIME(NOW(), \'0:05:00\')',
433 $size_mappings = array(
434 WSRequestFields
::PIC_SMALL
=> 'small',
435 WSRequestFields
::PIC_MEDIUM
=> 'medium',
436 WSRequestFields
::PIC_LARGE
=> 'large',
438 $size = $size_mappings[$field];
439 return pl_url("api/1/sharing/picture/$size/$token");
445 case WSRequestFields
::CURRENT_CITY
:
446 $address = $p->getMainAddress();
447 if ($address != null
&& $this->isVisible($address->pub
)) {
448 return $address->locality
;
452 case WSRequestFields
::CURRENT_COUNTRY
:
453 $address = $p->getMainAddress();
454 if ($address != null
&& $this->isVisible($address->pub
)) {
455 return $address->country
;
459 case WSRequestFields
::ADDRESS
:
460 $address = $p->getMainAddress();
461 if ($address != null
&& $this->isVisible($address->pub
)) {
462 return $this->addressToResponse($address);
468 case WSRequestFields
::CURRENT_COMPANY
:
469 $job = $p->getMainJob();
470 if ($job != null
&& $this->isVisible($job->pub
)) {
471 return $job->company
->name
;
475 case WSRequestFields
::JOB
:
476 $jobs = $p->getJobs(Profile
::JOBS_ALL
);
478 foreach ($jobs as $job) {
479 if ($this->isVisible($job->pub
)) {
480 $res[] = $this->jobToResponse($job);
484 case WSRequestFields
::MINI_RESUME
:
485 if ($this->isVisible(Visibility
::EXPORT_PRIVATE
)) {
492 case WSRequestFields
::GROUPS
:
494 if ($this->isVisible(Visibility
::EXPORT_PRIVATE
)) {
495 foreach ($p->owner()->groups(true
, true
) as $group) {
496 $groups[] = array('name' => $group['nom']);
500 case WSRequestFields
::FRIENDS
:
502 if ($this->isVisible(Visibility
::EXPORT_PRIVATE
)) {
503 $contacts = $p->owner()->iterContacts();
504 if ($contacts == null
) {
508 while ($contact = $contacts->next()) {
509 $cps = $contact->getPartnerSettings($this->partner
->id
);
510 if ($cps->sharing_visibility
->isVisible(Visibility
::EXPORT_PRIVATE
)) {
511 $friends[] = $cps->exposed_uid
;
516 case WSRequestFields
::NETWORKING
:
518 if ($this->isVisible(Visibility
::EXPORT_PRIVATE
)) {
519 foreach ($p->getNetworking(Profile
::NETWORKING_ALL
) as $nw) {
521 'network' => $nw['name'],
522 'login' => $nw['address'],
533 protected function jobToResponse($job)
536 'company' => $job->company
->name
,
537 'title' => $job->description
,
538 'sector' => array_pop($job->terms
),
542 foreach($job->phones() as $phone) {
543 if ($this->isVisible($phone->pub
)) {
544 $data['phone'] = $phone->display
;
548 if ($job->address
&& $this->isVisible($job->address
->pub
)) {
549 $data['address'] = $this->addressToResponse($job->address
);
554 protected function addressToResponse($address)
557 'street' => $address->postalText
,
558 'zipcode' => $address->postalCode
,
559 'city' => $address->locality
,
560 'country' => $address->country
,
561 'latitude' => $address->latitude
,
562 'longitude' => $address->longitude
,
568 // {{{ WSRequestCriteria
569 /** Holds all enums and related mappings for criterias.
571 class WSRequestCriteria
573 const FIRSTNAME
= 'firstname';
574 const LASTNAME
= 'lastname';
575 const SCHOOL
= 'school';
576 const DIPLOMA
= 'diploma';
577 const DIPLOMA_FIELD
= 'diploma_field';
578 const PROMOTION
= 'promotion';
579 const HOBBIES
= 'hobbies';
580 const ZIPCODE
= 'zipcode';
582 const COUNTRY
= 'country';
583 const JOB_CURRENT_SECTOR
= 'job_current_sector';
584 const JOB_CURRENT_TITLE
= 'job_current_title';
585 const JOB_CURRENT_COMPANY
= 'job_current_company';
586 const JOB_CURRENT_CITY
= 'job_current_city';
587 const JOB_CURRENT_COUNTRY
= 'job_current_country';
588 const JOB_ANY_SECTOR
= 'job_any_sector';
589 const JOB_ANY_COMPANY
= 'job_any_company';
590 const JOB_ANY_COUNTRY
= 'job_any_country';
591 const JOB_RESUME
= 'job_resume';
592 const JOB_COMPETENCIES
= 'job_competencies';
593 const PROFESSIONAL_PROJECT
= 'professional_project';
594 const ALT_DIPLOMA
= 'alt_diploma';
595 const NOT_UID
= 'not_uid';
597 public static $choices_simple = array(
606 self
::JOB_ANY_COUNTRY
,
607 self
::JOB_CURRENT_CITY
,
608 self
::JOB_CURRENT_COUNTRY
,
609 self
::JOB_ANY_COMPANY
,
610 self
::JOB_ANY_SECTOR
,
611 self
::JOB_CURRENT_COMPANY
,
612 self
::JOB_CURRENT_SECTOR
,
613 self
::JOB_CURRENT_TITLE
,
616 const SCHOOL_AGRO
= 'agro';
617 const SCHOOL_ENSAE
= 'ensae';
618 const SCHOOL_ENSCP
= 'enscp';
619 const SCHOOL_ENST
= 'enst';
620 const SCHOOL_ENSTA
= 'ensta';
621 const SCHOOL_ESPCI
= 'espci';
622 const SCHOOL_GADZ
= 'gadz';
623 const SCHOOL_HEC
= 'hec';
624 const SCHOOL_MINES
= 'ensmp';
625 const SCHOOL_PONTS
= 'enpc';
626 const SCHOOL_SUPELEC
= 'supelec';
627 const SCHOOL_SUPOP
= 'supop';
628 const SCHOOL_X
= 'X';
630 const DIPLOMA_ING
= 'ING';
631 const DIPLOMA_MASTER
= 'MASTER';
632 const DIPLOMA_PHD
= 'PHD';
634 public static $choices_enum = array(
635 self
::SCHOOL
=> array(
636 self
::SCHOOL_AGRO
=> false
,
637 self
::SCHOOL_ENSAE
=> false
,
638 self
::SCHOOL_ENSCP
=> false
,
639 self
::SCHOOL_ENST
=> false
,
640 self
::SCHOOL_ENSTA
=> false
,
641 self
::SCHOOL_ESPCI
=> false
,
642 self
::SCHOOL_GADZ
=> false
,
643 self
::SCHOOL_HEC
=> false
,
644 self
::SCHOOL_MINES
=> false
,
645 self
::SCHOOL_PONTS
=> false
,
646 self
::SCHOOL_SUPELEC
=> false
,
647 self
::SCHOOL_SUPOP
=> false
,
648 self
::SCHOOL_X
=> true
,
650 self
::DIPLOMA
=> array(
651 self
::DIPLOMA_ING
=> UserFilter
::GRADE_ING
,
652 self
::DIPLOMA_MASTER
=> UserFilter
::GRADE_MST
,
653 self
::DIPLOMA_PHD
=> UserFilter
::GRADE_PHD
,
657 public static $choices_list = array(
659 self
::JOB_COMPETENCIES
,
662 self
::PROFESSIONAL_PROJECT
,
667 // {{{ WSRequestFields
668 /** Holds all enums for fields.
670 class WSRequestFields
673 const FIRSTNAME
= 'firstname';
674 const LASTNAME
= 'lastname';
675 const BIRTHDATE
= 'birthdate';
676 const GENDER
= 'gender';
677 const FAMILY_POSITION
= 'family_position';
678 const SCHOOL
= 'school';
679 const DIPLOMA
= 'diploma';
680 const DIPLOMA_FIELD
= 'diploma_field';
681 const PROMOTION
= 'promotion';
682 const ALT_DIPLOMAS
= 'alt_diplomas';
683 const CURRENT_COMPANY
= 'current_company';
684 const CURRENT_CITY
= 'current_city';
685 const CURRENT_COUNTRY
= 'current_country';
686 const MOBILE_PHONE
= 'mobile_phone';
687 const HONORARY_TITLES
= 'honorary_titles';
688 const EMAIL
= 'email';
689 const PIC_SMALL
= 'pic_small';
690 const PIC_MEDIUM
= 'pic_medium';
691 const PIC_LARGE
= 'pic_large';
692 const ADDRESS
= 'address';
694 const GROUPS
= 'groups';
695 const LANGS
= 'langs';
696 const JOB_COMPETENCIES
= 'job_competencies';
697 const MINI_RESUME
= 'mini_resume';
698 const RESUME
= 'resume';
699 const PROFESSIONAL_PROJECT
= 'professional_project';
700 const HOBBIES
= 'hobbies';
701 const FRIENDS
= 'friends';
702 const NETWORKING
= 'networking';
704 const GENDER_MAN
= 'man';
705 const GENDER_WOMAN
= 'woman';
707 const DIPLOMA_ING
= 'engineer';
708 const DIPLOMA_MASTER
= 'master';
709 const DIPLOMA_PHD
= 'phd';
711 public static $choices = array(
717 self
::FAMILY_POSITION
,
723 self
::CURRENT_COMPANY
,
725 self
::CURRENT_COUNTRY
,
727 self
::HONORARY_TITLES
,
736 self
::JOB_COMPETENCIES
,
739 self
::PROFESSIONAL_PROJECT
,
745 public static function profileDegreeToWSDiploma($degree)
748 case Profile
::DEGREE_X
:
749 return self
::DIPLOMA_ING
;
750 case Profile
::DEGREE_M
:
751 return self
::DIPLOMA_MASTER
;
752 case Profile
::DEGREE_D
:
753 return self
::DIPLOMA_PHD
;
762 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker fenc=utf-8: