changed git call from https to git readonly
[atutor.git] / mods / social / lib / Shindig / ATutorDbFetcher.php
1 <?php
2 // $Id$
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 class ATutorDbFetcher {
23   private $url_prefix;
24   private $cache;
25   public $db;
26
27   function debug($var, $title='') {
28         echo '<pre style="border: 1px black solid; padding: 0px; margin: 10px;" title="debugging box">';
29         if ($title) {
30                 echo '<h4>'.$title.'</h4>';
31         }
32         
33         ob_start();
34         print_r($var);
35         $str = ob_get_contents();
36         ob_end_clean();
37
38         $str = str_replace('<', '&lt;', $str);
39
40         $str = str_replace('[', '<span style="color: red; font-weight: bold;">[', $str);
41         $str = str_replace(']', ']</span>', $str);
42         $str = str_replace('=>', '<span style="color: blue; font-weight: bold;">=></span>', $str);
43         $str = str_replace('Array', '<span style="color: purple; font-weight: bold;">Array</span>', $str);
44         echo $str;
45         echo '</pre>';
46 }
47
48   // Singleton
49   private static $fetcher;
50
51   private function connectDb() {
52     // one of the class paths should point to ATutor's document root, abuse that fact to find our config
53     $extension_class_paths = Config::get('extension_class_paths');
54     foreach (explode(',', $extension_class_paths) as $path) {
55       if (file_exists($path . "/ATutorDbFetcher.php")) {
56         $configFile = $path . '/../../../../include/lib/mysql_connect.inc.php';
57         if (file_exists($configFile)) {
58                   define('AT_INCLUDE_PATH', $path . '/../../../../include/');
59                   include(AT_INCLUDE_PATH.'config.inc.php');
60                   include(AT_INCLUDE_PATH . 'lib/constants.inc.php');
61                   include(AT_INCLUDE_PATH . 'lib/mysql_connect.inc.php');
62                   $this->db = $db;
63           break;
64         }
65       }
66     }
67     if (! isset($configFile)) {
68       throw new Exception("Could not locate ATutor's configuration file while scanning extension_class_paths ({$extension_class_paths})");
69     }
70 //    $this->db = mysqli_connect($config['db_host'], $config['db_user'], $config['db_passwd'], $config['db_database']);
71 //    mysqli_select_db($this->db, $config['db_database']);
72 //    $this->url_prefix = $config['partuza_url'];
73   }
74
75   private function __construct() {
76     $cache = Config::get('data_cache');
77     $this->cache = new $cache();
78   }
79
80   private function checkDb() {
81     if (! is_resource($this->db)) {
82       $this->connectDb();
83     }
84   }
85
86   private function __clone() {  // private, don't allow cloning of a singleton
87   }
88
89   static function get() {
90     // This object is a singleton
91     if (! isset(ATutorDbFetcher::$fetcher)) {
92       ATutorDbFetcher::$fetcher = new ATutorDbFetcher();
93     }
94     return ATutorDbFetcher::$fetcher;
95   }
96
97   public function createActivity($member_id, $activity, $app_id = '0') {
98     $this->checkDb();
99     $app_id = intval($app_id);
100     $person_id = intval($member_id);
101     $title = trim(isset($activity['title']) ? $activity['title'] : '');
102     if (empty($title)) {
103       throw new Exception("Invalid activity: empty title");
104     }
105 //    $body = isset($activity['body']) ? $activity['body'] : '';
106     $title = mysql_real_escape_string($title);
107 //    $body = mysql_real_escape_string($body);
108         $sql = "insert into ".TABLE_PREFIX."social_activities (id, member_id, application_id, title, created_date) values (0, $member_id, $app_id, '$title', NOW())";
109     mysql_query($sql, $this->db);
110     if (! ($activityId = mysql_insert_id($this->db))) {
111       return false;
112     }
113
114 /**
115  * I don't have this on my system yet. -Harris
116
117     $mediaItems = isset($activity['mediaItems']) ? $activity['mediaItems'] : array();
118     if (count($mediaItems)) {
119       foreach ($mediaItems as $mediaItem) {
120         $type = isset($mediaItem['type']) ? $mediaItem['type'] : '';
121         $mimeType = isset($mediaItem['mimeType']) ? $mediaItem['mimeType'] : '';
122         $url = isset($mediaItem['url']) ? $mediaItem['url'] : '';
123         $type = mysqli_real_escape_string($this->db, trim($type));
124         $mimeType = mysqli_real_escape_string($this->db, trim($mimeType));
125         $url = mysqli_real_escape_string($this->db, trim($url));
126         if (! empty($mimeType) && ! empty($type) && ! empty($url)) {
127           mysqli_query($this->db, "insert into activity_media_items (id, activity_id, mime_type, media_type, url) values (0, $activityId, '$mimeType', '$type', '$url')");
128           if (! mysqli_insert_id($this->db)) {
129             return false;
130           }
131         } else {
132           return false;
133         }
134       }
135     }
136 */
137     return true;
138   }
139
140 //  public function getActivities($ids, $appId, $sortBy, $filterBy, $filterOp, $filterValue, $startIndex, $count, $fields, $activityIds) {
141   public function getActivities($ids, $appId, $sortBy, $filterBy, $filterOp, $filterValue, $startIndex, $count, $fields) {
142         global $db;
143     //TODO add support for filterBy, filterOp and filterValue
144     $this->checkDb();
145     $activities = array();
146     foreach ($ids as $key => $val) {
147       $ids[$key] = mysql_real_escape_string($val);
148     }
149     $ids = implode(',', $ids);
150     if (isset($activityIds) && is_array($activityIds)) {
151       foreach ($activityIds as $key => $val) {
152         $activityIds[$key] = mysql_real_escape_string($val);
153       }
154       $activityIdQuery = " and activities.id in (".implode(',', $activityIds);
155     } else {
156       $activityIdQuery = '';
157     }
158     // return a proper totalResults count
159         $sql = "select count(id) from ".TABLE_PREFIX."social_activities where ".TABLE_PREFIX."activities.person_id in ($ids) $activityIdQuery";
160     $res = mysql_query($sql, $this->db);
161
162     if ($res !== false) {
163       list($totalResults) = mysql_fetch_row($res);
164     } else {
165       $totalResults = '0';
166     }
167     $startIndex = (! is_null($startIndex) && $startIndex !== false && is_numeric($startIndex)) ? intval($startIndex) : '0';
168     $count = (! is_null($count) && $count !== false && is_numeric($count)) ? intval($count) : '20';
169     $activities['totalResults'] = $totalResults;
170     $activities['startIndex'] = $startIndex;
171     $activities['count'] = $count;
172     $query = "
173                         select 
174                                 ".TABLE_PREFIX."social_activities.member_id as member_id,
175                                 ".TABLE_PREFIX."social_activities.id as activity_id,
176                                 ".TABLE_PREFIX."social_activities.title as title,
177                                 ".TABLE_PREFIX."social_activities.created as created
178                         from 
179                                 ".TABLE_PREFIX."social_activities
180                         where
181                                 ".TABLE_PREFIX."social_activities.member_id in ($ids)
182                                 $activityIdQuery
183                         order by 
184                                 created desc
185                         limit 
186                                 $startIndex, $count
187                         ";
188     $res = mysql_query($query, $this->db);
189     if ($res) {
190       if (@mysql_num_rows($res)) {
191         while ($row = @mysql_fetch_assoc($res)) {
192           $activity = new Activity($row['activity_id'], $row['member_id']);
193           $activity->setStreamTitle('activities');
194           $activity->setTitle($row['activity_title']);
195 //          $activity->setBody($row['activity_body']);
196           $activity->setPostedTime($row['created']);
197           $activity->setMediaItems($this->getMediaItems($row['activity_id']));
198           $activities[] = $activity;
199         }
200       } elseif (isset($activityIds) && is_array($activityIds)) {
201         // specific activity id was specified, return a not found flag
202         return false;
203       }
204       return $activities;
205     } else {
206       return false;
207     }
208   }
209
210   public function deleteActivities($userId, $appId, $activityIds) {
211     $this->checkDb();
212     foreach ($activityIds as $key => $val) {
213       $activityIds[$key] = mysql_real_escape_string($val);
214     }
215     $activityIds = implode(',', $activityIds);
216     $userId = intval($userId);
217     $appId = intval($appId);
218         //can use this instead:         $sql = "delete from ".TABLE_PREFIX."social_activities where id in ($activityIds)";
219         $sql = "delete from ".TABLE_PREFIX."social_activities where member_id = $userId and application_id = $appId and id in ($activityIds)";
220         
221     mysql_query($sql, $this->db);
222     return (mysql_affected_rows($this->db) != 0);
223   }
224
225 /**
226   * I didn't implement this yet
227   */
228   private function getMediaItems($activity_id) {
229     $media = array();
230 //    $activity_id = mysqli_real_escape_string($db, $activity_id);
231 //    $res = mysqli_query($this->db, "select mime_type, media_type, url from ".TABLE_PREFIX."activity_media_items where activity_id = $activity_id");
232 //    while (list($mime_type, $type, $url) = @mysqli_fetch_row($res)) {
233 //      $media[] = new MediaItem($mime_type, $type, $url);
234 //    }
235     return $media;
236   }  
237
238   public function getFriendIds($member_id) {
239         global $db;
240     $this->checkDb();
241     $ret = array();
242     $person_id = intval($person_id);
243         $sql = "select member_id, friend_id from ".TABLE_PREFIX."social_friends where member_id = $member_id or friend_id = $member_id";
244     $res = mysql_query($sql, $this->db);
245     while (list($mid, $fid) = mysql_fetch_row($res)) {
246       $id = ($mid == $member_id) ? $fid : $mid;
247       $ret[] = $id;
248     }
249     return $ret;
250   }
251
252   public function setAppData($member_id, $key, $value, $app_id) {
253         $this->checkDb();
254     $member_id = intval($member_id);
255     $key = mysql_real_escape_string($key);
256     $value = mysql_real_escape_string($value);
257     $app_id = intval($app_id);
258     if (empty($value)) {
259       // empty key kind of became to mean "delete data" (was an old orkut hack that became part of the spec spec)
260           $sql = "delete from ".TABLE_PREFIX."social_application_settings where application_id = $app_id and member_id = $member_id and name = '$key'";
261       if (! @mysql_query($sql, $this->db)) {
262         return false;
263       }
264     } else {
265                 $sql ="insert into ".TABLE_PREFIX."social_application_settings (application_id, member_id, name, value) values ($app_id, $member_id, '$key', '$value') on duplicate key update value = '$value'";
266       if (! @mysql_query($sql, $this->db)) {
267         return false;
268       }
269     }
270     return true;
271   }
272
273   public function deleteAppData($member_id, $key, $app_id) {
274     global $db;
275         $this->checkDb();
276     $person_id = intval($member_id);
277     $app_id = intval($app_id);
278     if ($key == '*') {
279                 $sql = "delete from ".TABLE_PREFIX."social_application_settings where application_id = $app_id and member_id = $member_id";
280       if (! @mysql_query($sql, $this->db)) {
281         return false;
282       }
283     } else {
284       $key = mysql_real_escape_string($this->db, $key);
285           $sql = "delete from ".TABLE_PREFIX."social_application_settings where application_id = $app_id and member_id = $member_id and name = '$key'";
286       if (! @mysql_query($sql, $this->db)) {
287         return false;
288       }
289     }
290     return true;
291   }
292
293   public function getAppData($ids, $keys, $app_id) {
294     $this->checkDb();
295     $data = array();
296     foreach ($ids as $key => $val) {
297       if (! empty($val)) {
298         $ids[$key] = intval($val);
299       }
300     }
301     if (! isset($keys[0])) {
302       $keys[0] = '*';
303     }
304     if ($keys[0] == '*') {
305       $keys = '';
306     } elseif (is_array($keys)) {
307       foreach ($keys as $key => $val) {
308         $keys[$key] = "'" . addslashes($val) . "'";
309       }
310       $keys = "and name in (" . implode(',', $keys) . ")";
311     } else {
312       $keys = '';
313     }
314         $sql = "select member_id, name, value from ".TABLE_PREFIX."social_application_settings where application_id = $app_id and member_id in (" . implode(',', $ids) . ") $keys";
315     $res = mysql_query($sql, $this->db);
316     while (list($member_id, $key, $value) = mysql_fetch_row($res)) {
317       if (! isset($data[$member_id])) {
318         $data[$member_id] = array();
319       }
320       $data[$member_id][$key] = $value;
321     }
322     return $data;
323   }
324
325   public function getPeople($ids, $fields, $options, $token) {
326         $first = $options->getStartIndex();
327     $max = $options->getCount();
328     $this->checkDb();
329     $ret = array();
330     $filterQuery = '';
331     if ($options->getFilterBy() == 'hasApp') {
332       // remove the filterBy field, it's taken care of in the query already, otherwise filterResults will disqualify all results
333       $options->setFilterBy(null);
334       $appId = $token->getAppId();
335       $filterQuery = " and id in (select member_id from ".TABLE_PREFIX."social_applications where application_id = $appId)";
336     } elseif ($options->getFilterBy() == 'all') {
337       $options->setFilterBy(null);
338     }
339     $query = "SELECT member.*, info.interests, info.associations, info.awards FROM ".TABLE_PREFIX."members member LEFT JOIN ".TABLE_PREFIX."social_member_additional_information info ON member.member_id=info.member_id WHERE  member.member_id IN (" . implode(',', $ids) . ") $filterQuery ORDER BY member.member_id ";
340
341     $res = mysql_query($query, $this->db);
342     if ($res) {
343       while ($row = mysql_fetch_assoc($res)) {
344         $member_id = intval($row['member_id']);
345         $name = new Name($row['first_name'] . ' ' . $row['last_name']);
346
347         $name->setGivenName($row['first_name']);
348         $name->setFamilyName($row['last_name']);
349         $person = new Person($row['member_id'], $name);
350         $person->setDisplayName($name->getFormatted());
351         $person->setAboutMe($row['about_me']);
352         $person->setAge($row['age']);
353         $person->setChildren($row['children']);
354         $person->setBirthday(date('Y-m-d', $row['date_of_birth']));
355         $person->setEthnicity($row['ethnicity']);
356         $person->setFashion($row['fashion']);
357         $person->setHappiestWhen($row['happiest_when']);
358         $person->setHumor($row['humor']);
359         $person->setJobInterests($row['job_interests']);
360         $person->setLivingArrangement($row['living_arrangement']);
361         $person->setLookingFor($row['looking_for']);
362         $person->setNickname($row['nickname']);
363         $person->setPets($row['pets']);
364         $person->setPoliticalViews($row['political_views']);
365         $person->setProfileSong($row['profile_song']);
366         $person->setProfileUrl($this->url_prefix . '/profile/' . $row['member_id']);
367         $person->setProfileVideo($row['profile_video']);
368         $person->setRelationshipStatus($row['relationship_status']);
369         $person->setReligion($row['religion']);
370         $person->setRomance($row['romance']);
371         $person->setScaredOf($row['scared_of']);
372         $person->setSexualOrientation($row['sexual_orientation']);
373         $person->setStatus($row['status']);
374         $person->setThumbnailUrl(! empty($row['thumbnail_url']) ? $this->url_prefix . $row['thumbnail_url'] : '');
375
376         if (! empty($row['thumbnail_url'])) {
377           // also report thumbnail_url in standard photos field (this is the only photo supported by ATutor)
378           $person->setPhotos(array(
379               new Photo($this->url_prefix . 'get_profile_img.php?id='.$row['member_id'], 'thumbnail', true)));
380         }
381         $person->setUtcOffset(sprintf('%+03d:00', $row['time_zone'])); // force "-00:00" utc-offset format
382         if (! empty($row['drinker'])) {
383           $person->setDrinker($row['drinker']);
384         }
385         if (! empty($row['gender'])) {
386           $person->setGender(strtolower($row['gender']));
387         }
388                 if (! empty($row['email'])){
389                   //TODO: Assumed <static> object TYPE to be "home".  Change it if ATutor starts accepting more than one email
390                   $email = new Email(strtolower($row['email']), 'home');
391           $person->setEmails($email);
392                 }
393                 if (! empty($row['interests'])){
394           $strings = explode(',', $row['interests']);
395           $person->setInterests($strings);
396                 }
397
398                 //TODO: Not in ATutor yet, skeleton field
399         if (! empty($row['smoker'])) {
400           $person->setSmoker($row['smoker']);
401         }
402         /* the following fields require additional queries so are only executed if requested */
403         if (isset($fields['activities']) || isset($fields['@all'])) {
404           $activities = array();
405                   $sql = "select title from ".TABLE_PREFIX."social_activities where member_id = " . $member_id;
406           $res2 = mysql_query($sql, $this->db);
407
408           while (list($activity) = mysql_fetch_row($res2)) {
409             $activities[] = $activity;
410           }
411           $person->setActivities($activities);
412         }
413
414         if (isset($fields['addresses']) || isset($fields['@all'])) {
415           $addresses = array();
416                   $sql = "select address, postal, city, province, country from ".TABLE_PREFIX."members m where m.member_id = " . $member_id;
417           $res2 = mysql_query($sql, $this->db);
418           while ($row = mysql_fetch_assoc($res2)) {
419             if (empty($row['unstructured_address'])) {
420               $row['unstructured_address'] = trim($row['street_address'] . " " . $row['province'] . " " . $row['country']);
421             }
422             $addres = new Address($row['unstructured_address']);
423             $addres->setCountry($row['country']);
424             $addres->setLatitude($row['latitude']);
425             $addres->setLongitude($row['longitude']);
426             $addres->setLocality($row['locality']);
427             $addres->setPostalCode($row['postal_code']);
428             $addres->setRegion($row['province']);
429             $addres->setStreetAddress($row['street_address']);
430             $addres->setType($row['address_type']);
431             //FIXME quick and dirty hack to demo PC
432             $addres->setPrimary(true);
433             $addresses[] = $addres;
434           }
435           $person->setAddresses($addresses);
436         }
437                 //TODO: Not in ATutor yet, skeleton field
438         if (isset($fields['bodyType']) || isset($fields['@all'])) {
439           $res2 = mysqli_query($db, "select * from ".TABLE_PREFIX."person_body_type where person_id = " . $person_id);
440           if (@mysqli_num_rows($res2)) {
441             $row = @mysql_fetch_array($res2, MYSQLI_ASSOC);
442             $bodyType = new BodyType();
443             $bodyType->setBuild($row['build']);
444             $bodyType->setEyeColor($row['eye_color']);
445             $bodyType->setHairColor($row['hair_color']);
446             $bodyType->setHeight($row['height']);
447             $bodyType->setWeight($row['weight']);
448             $person->setBodyType($bodyType);
449           }
450         }
451                 //TODO: Not in ATutor yet, skeleton field
452         if (isset($fields['books']) || isset($fields['@all'])) {
453           $books = array();
454           $res2 = mysqli_query($db, "select book from ".TABLE_PREFIX."person_books where person_id = " . $person_id);
455           while (list($book) = @mysqli_fetch_row($res2)) {
456             $books[] = $book;
457           }
458           $person->setBooks($books);
459         }
460                 //TODO: Not in ATutor yet, skeleton field
461         if (isset($fields['cars']) || isset($fields['@all'])) {
462           $cars = array();
463           $res2 = mysqli_query($db, "select car from ".TABLE_PREFIX."person_cars where person_id = " . $person_id);
464           while (list($car) = @mysqli_fetch_row($res2)) {
465             $cars[] = $car;
466           }
467           $person->setCars($cars);
468         }
469                 //TODO: Not in ATutor yet, skeleton field
470         if (isset($fields['currentLocation']) || isset($fields['@all'])) {
471           $addresses = array();
472           $res2 = mysqli_query($db, "select a.* from ".TABLE_PREFIX."person_current_location pcl, ".TABLE_PREFIX."person_addresses pa, ".TABLE_PREFIX."addresses a where a.id = pcl.address_id and pa.person_id = " . $person_id);
473           if (@mysqli_num_rows($res2)) {
474             $row = mysqli_fetch_array($res2, MYSQLI_ASSOC);
475             if (empty($row['unstructured_address'])) {
476               $row['unstructured_address'] = trim($row['street_address'] . " " . $row['region'] . " " . $row['country']);
477             }
478             $addres = new Address($row['unstructured_address']);
479             $addres->setCountry($row['country']);
480             $addres->setLatitude($row['latitude']);
481             $addres->setLongitude($row['longitude']);
482             $addres->setLocality($row['locality']);
483             $addres->setPostalCode($row['postal_code']);
484             $addres->setRegion($row['region']);
485             $addres->setStreetAddress($row['street_address']);
486             $addres->setType($row['address_type']);
487             $person->setCurrentLocation($addres);
488           }
489         }
490                 //TODO: Email is a singleton in ATutor, expand it.  A person may have 1+ emails nowadays.
491                 //added to the above with all the other member's properties
492 /*
493         if (isset($fields['emails']) || isset($fields['@all'])) {
494           $emails = array();
495                   $sql = "select address, email_type from ".TABLE_PREFIX."person_emails where person_id = " . $person_id;
496           $res2 = mysql_query();
497           while (list($address, $type) = @mysqli_fetch_row($res2)) {
498             $emails[] = new Email(strtolower($address), $type); // TODO: better email canonicalization; remove dups
499           }
500           $person->setEmails($emails);
501         }
502 */
503                 //TODO: Not in ATutor yet, skeleton field
504         if (isset($fields['food']) || isset($fields['@all'])) {
505           $foods = array();
506           $res2 = mysqli_query($db, "select food from ".TABLE_PREFIX."person_foods where person_id = " . $person_id);
507           while (list($food) = @mysqli_fetch_row($res2)) {
508             $foods[] = $food;
509           }
510           $person->setFood($foods);
511         }
512                 //TODO: Not in ATutor yet, skeleton field
513         if (isset($fields['heroes']) || isset($fields['@all'])) {
514           $strings = array();
515           $res2 = mysqli_query($db, "select hero from ".TABLE_PREFIX."person_heroes where person_id = " . $person_id);
516           while (list($data) = @mysqli_fetch_row($res2)) {
517             $strings[] = $data;
518           }
519           $person->setHeroes($strings);
520         }
521                 //Added with the above profile, interests is in CSV
522 /*
523         if (isset($fields['interests']) || isset($fields['@all'])) {
524           $strings = array();
525           $res2 = mysqli_query($db, "select interest from ".TABLE_PREFIX."person_interests where person_id = " . $person_id);
526           while (list($data) = @mysqli_fetch_row($res2)) {
527             $strings[] = $data;
528           }
529           $person->setInterests($strings);
530         }
531 */
532         $organizations = array();
533         $fetchedOrg = false;
534         if (isset($fields['jobs']) || isset($fields['@all'])) {
535                   $sql = "SELECT * FROM ". TABLE_PREFIX . "social_member_position WHERE member_id = ".$member_id;
536           $res2 = mysql_query($sql, $this->db);
537           while ($row = mysql_fetch_assoc($res2)) {
538             $organization = new Organization();
539             $organization->setDescription($row['description']);
540             $organization->setEndDate($row['to']);
541             $organization->setField($row['field']);
542             $organization->setName($row['company']);
543             $organization->setSalary($row['salary']);
544             $organization->setStartDate($row['from']);
545             $organization->setSubField($row['sub_field']); 
546             $organization->setTitle($row['title']);
547             $organization->setWebpage($row['webpage']);
548             $organization->setType('job');
549
550                         //TODO: Address: To be implemented
551                         /*
552             if ($row['address_id']) {
553               $res3 = mysqli_query($db, "select * from ".TABLE_PREFIX."addresses where id = " . mysqli_real_escape_string($db, $row['address_id']));
554               if (mysqli_num_rows($res3)) {
555                 $row = mysqli_fetch_array($res3, MYSQLI_ASSOC);
556                 if (empty($row['unstructured_address'])) {
557                   $row['unstructured_address'] = trim($row['street_address'] . " " . $row['region'] . " " . $row['country']);
558                 }
559                 $addres = new Address($row['unstructured_address']);
560                 $addres->setCountry($row['country']);
561                 $addres->setLatitude($row['latitude']);
562                 $addres->setLongitude($row['longitude']);
563                 $addres->setLocality($row['locality']);
564                 $addres->setPostalCode($row['postal_code']);
565                 $addres->setRegion($row['region']);
566                 $addres->setStreetAddress($row['street_address']);
567                 $addres->setType($row['address_type']);
568                 $organization->setAddress($address);
569               }
570             }
571                         */
572             $organizations[] = $organization;
573           }
574           $fetchedOrg = true;
575         }
576         if (isset($fields['schools']) || isset($fields['@all'])) {
577           $res2 = mysqli_query($db, "select o.* from ".TABLE_PREFIX."person_schools ps, ".TABLE_PREFIX."organizations o where o.id = ps.organization_id and ps.person_id = " . $person_id);
578           while ($row = mysqli_fetch_array($res2, MYSQLI_ASSOC)) {
579             $organization = new Organization();
580             $organization->setDescription($row['description']);
581             $organization->setEndDate($row['to']);
582             $organization->setField($row['field']);
583             $organization->setName($row['university']);
584             $organization->setSalary($row['salary']);
585             $organization->setStartDate($row['from']);
586             $organization->setSubField($row['sub_field']);
587             $organization->setTitle($row['degree']);
588             $organization->setWebpage($row['webpage']);
589             $organization->setType($row['school']);
590                         //TODO: Address: To be implemented
591                         /*
592             if ($row['address_id']) {
593               $res3 = mysqli_query($db, "select * from ".TABLE_PREFIX."addresses where id = " . mysqli_real_escape_string($db, $row['address_id']));
594               if (mysqli_num_rows($res3)) {
595                 $row = mysqli_fetch_array($res3, MYSQLI_ASSOC);
596                 if (empty($row['unstructured_address'])) {
597                   $row['unstructured_address'] = trim($row['street_address'] . " " . $row['region'] . " " . $row['country']);
598                 }
599                 $addres = new Address($row['unstructured_address']);
600                 $addres->setCountry($row['country']);
601                 $addres->setLatitude($row['latitude']);
602                 $addres->setLongitude($row['longitude']);
603                 $addres->setLocality($row['locality']);
604                 $addres->setPostalCode($row['postal_code']);
605                 $addres->setRegion($row['region']);
606                 $addres->setStreetAddress($row['street_address']);
607                 $addres->setType($row['address_type']);
608                 $organization->setAddress($address);
609               }
610             }
611                         */
612             $organizations[] = $organization;
613           }
614           $fetchedOrg = true;
615         }
616         if ($fetchedOrg) {
617           $person->setOrganizations($organizations);
618         }
619         //TODO languagesSpoken, currently missing the languages / countries tables so can't do this yet
620                 //TODO: Not in ATutor yet, skeleton field
621         if (isset($fields['movies']) || isset($fields['@all'])) {
622           $strings = array();
623           $res2 = mysqli_query($db, "select movie from ".TABLE_PREFIX."person_movies where person_id = " . $person_id);
624           while (list($data) = @mysqli_fetch_row($res2)) {
625             $strings[] = $data;
626           }
627           $person->setMovies($strings);
628         }
629         if (isset($fields['music']) || isset($fields['@all'])) {
630           $strings = array();
631           $res2 = mysqli_query($db, "select music from ".TABLE_PREFIX."person_music where person_id = " . $person_id);
632           while (list($data) = @mysqli_fetch_row($res2)) {
633             $strings[] = $data;
634           }
635           $person->setMusic($strings);
636         }
637         if (isset($fields['phoneNumbers']) || isset($fields['@all'])) {
638           $numbers = array();
639           $res2 = mysqli_query($db, "select number, number_type from ".TABLE_PREFIX."person_phone_numbers where person_id = " . $person_id);
640           while (list($number, $type) = @mysqli_fetch_row($res2)) {
641             $numbers[] = new Phone($number, $type);
642           }
643           $person->setPhoneNumbers($numbers);
644         }
645         if (isset($fields['ims']) || isset($fields['@all'])) {
646           $ims = array();
647           $res2 = mysqli_query($db, "select value, value_type from ".TABLE_PREFIX."person_ims where person_id = " . $person_id);
648           while (list($value, $type) = @mysqli_fetch_row($res2)) {
649             $ims[] = new Im($value, $type);
650           }
651           $person->setIms($ims);
652         }
653         if (isset($fields['accounts']) || isset($fields['@all'])) {
654           $accounts = array();
655           $res2 = mysqli_query($db, "select domain, userid, username from ".TABLE_PREFIX."person_accounts where person_id = " . $person_id);
656           while (list($domain, $userid, $username) = @mysqli_fetch_row($res2)) {
657             $accounts[] = new Account($domain, $userid, $username);
658           }
659           $person->setAccounts($accounts);
660         }
661         if (isset($fields['quotes']) || isset($fields['@all'])) {
662           $strings = array();
663           $res2 = mysqli_query($db, "select quote from ".TABLE_PREFIX."person_quotes where person_id = " . $person_id);
664           while (list($data) = @mysqli_fetch_row($res2)) {
665             $strings[] = $data;
666           }
667           $person->setQuotes($strings);
668         }
669         if (isset($fields['sports']) || isset($fields['@all'])) {
670           $strings = array();
671           $res2 = mysqli_query($db, "select sport from ".TABLE_PREFIX."person_sports where person_id = " . $person_id);
672           while (list($data) = @mysqli_fetch_row($res2)) {
673             $strings[] = $data;
674           }
675           $person->setSports($strings);
676         }
677         if (isset($fields['tags']) || isset($fields['@all'])) {
678           $strings = array();
679           $res2 = mysqli_query($db, "select tag from ".TABLE_PREFIX."person_tags where person_id = " . $person_id);
680           while (list($data) = @mysqli_fetch_row($res2)) {
681             $strings[] = $data;
682           }
683           $person->setTags($strings);
684         }
685         
686         if (isset($fields['turnOns']) || isset($fields['@all'])) {
687           $strings = array();
688           $res2 = mysqli_query($db, "select turn_on from ".TABLE_PREFIX."person_turn_ons where person_id = " . $person_id);
689           while (list($data) = @mysqli_fetch_row($res2)) {
690             $strings[] = $data;
691           }
692           $person->setTurnOns($strings);
693         }
694         if (isset($fields['turnOffs']) || isset($fields['@all'])) {
695           $strings = array();
696           $res2 = mysqli_query($db, "select turn_off from ".TABLE_PREFIX."person_turn_offs where person_id = " . $person_id);
697           while (list($data) = @mysqli_fetch_row($res2)) {
698             $strings[] = $data;
699           }
700           $person->setTurnOffs($strings);
701         }
702         if (isset($fields['urls']) || isset($fields['@all'])) {
703           $strings = array();
704           $res2 = mysqli_query($db, "select url from ".TABLE_PREFIX."person_urls where person_id = " . $person_id);
705           while (list($data) = @mysqli_fetch_row($res2)) {
706             $strings[] = new Url($data, null, null);
707           }
708           $strings[] = new Url($this->url_prefix . '/profile/' . $member_id, null, 'profile'); // always include profile URL
709           $person->setUrls($strings);
710         }
711         $ret[$member_id] = $person;
712       }
713     }
714
715     try {
716       $ret = $this->filterResults($ret, $options);
717       $ret['totalSize'] = count($ret);
718     } catch(Exception $e) {
719       $ret['totalSize'] = count($ret) - 1;
720       $ret['filtered'] = 'false';
721     }
722     if ($first !== false && $max !== false && is_numeric($first) && is_numeric($max) && $first >= 0 && $max > 0) {
723       $count = 0;
724       $result = array();
725       foreach ($ret as $id => $person) {
726         if ($id == 'totalSize' || $id == 'filtered') {
727           $result[$id] = $person;
728           continue;
729         }
730         if ($count >= $first && $count < $first + $max) {
731           $result[$id] = $person;
732         }
733         ++$count;
734       }
735       return $result;
736     } else {
737       return $ret;
738     }
739   }
740
741   private function filterResults($peopleById, $options) {
742     if (! $options->getFilterBy()) {
743       return $peopleById; // no filtering specified
744     }
745     $filterBy = $options->getFilterBy();
746     $op = $options->getFilterOperation();
747     if (! $op) {
748       $op = CollectionOptions::FILTER_OP_EQUALS; // use this container-specific default
749     }
750     $value = $options->getFilterValue();
751     $filteredResults = array();
752     $numFilteredResults = 0;
753     foreach ($peopleById as $id => $person) {
754       if ($person instanceof Person) {
755         if ($this->passesFilter($person, $filterBy, $op, $value)) {
756           $filteredResults[$id] = $person;
757           $numFilteredResults ++;
758         }
759       } else {
760         $filteredResults[$id] = $person; // copy extra metadata verbatim
761       }
762     }
763     if (! isset($filteredResults['totalSize'])) {
764       $filteredResults['totalSize'] = $numFilteredResults;
765     }
766     return $filteredResults;
767   }
768
769   private function passesFilter($person, $filterBy, $op, $value) {
770     $fieldValue = $person->getFieldByName($filterBy);
771     if ($fieldValue instanceof ComplexField) {
772       $fieldValue = $fieldValue->getPrimarySubValue();
773     }
774     if (! $fieldValue || (is_array($fieldValue) && ! count($fieldValue))) {
775       return false; // person is missing the field being filtered for
776     }
777     if ($op == CollectionOptions::FILTER_OP_PRESENT) {
778       return true; // person has a non-empty value for the requested field
779     }
780     if (! $value) {
781       return false; // can't do an equals/startswith/contains filter on an empty filter value
782     }
783     // grab string value for comparison
784     if (is_array($fieldValue)) {
785       // plural fields match if any instance of that field matches
786       foreach ($fieldValue as $field) {
787         if ($field instanceof ComplexField) {
788           $field = $field->getPrimarySubValue();
789         }
790         if ($this->passesStringFilter($field, $op, $value)) {
791           return true;
792         }
793       }
794     } else {
795       return $this->passesStringFilter($fieldValue, $op, $value);
796     }    
797     return false;
798   }
799
800   private function passesStringFilter($fieldValue, $op, $filterValue) {
801     switch ($op) {
802       case CollectionOptions::FILTER_OP_EQUALS:
803         return $fieldValue == $filterValue;
804       case CollectionOptions::FILTER_OP_CONTAINS:
805         return stripos($fieldValue, $filterValue) !== false;
806       case CollectionOptions::FILTER_OP_STARTSWITH:
807         return stripos($fieldValue, $filterValue) === 0;
808       default:
809         throw new Exception('unrecognized filterOp');
810     }
811   }
812 }