+
+ public static function rebuildSearchTokens($pid)
+ {
+ XDB::execute('DELETE FROM search_name
+ WHERE pid = {?}',
+ $pid);
+ $keys = XDB::iterator("SELECT CONCAT(n.particle, n.name) AS name, e.score,
+ FIND_IN_SET('public', e.flags) AS public
+ FROM profile_name AS n
+ INNER JOIN profile_name_enum AS e ON (n.typeid = e.id)
+ WHERE n.pid = {?}",
+ $pid);
+
+ while ($key = $keys->next()) {
+ if ($key['name'] == '') {
+ continue;
+ }
+ $toks = preg_split('/[ \'\-]+/', $key['name']);
+ $token = '';
+ $first = 5;
+ while ($toks) {
+ $token = strtolower(replace_accent(array_pop($toks) . $token));
+ $score = ($toks ? 0 : 10 + $first) * ($key['score'] / 10);
+ XDB::execute('REPLACE INTO search_name (token, pid, soundex, score, flags)
+ VALUES ({?}, {?}, {?}, {?}, {?})',
+ $token, $pid, soundex_fr($token), $score, $key['public']);
+ $first = 0;
+ }
+ }
+ }
+
+ /** The school identifier consists of 6 digits. The first 3 represent the
+ * promotion entry year. The last 3 indicate the student's rank.
+ *
+ * Our identifier consists of 8 digits and both half have the same role.
+ * This enables us to deal with bigger promotions and with a wider range
+ * of promotions.
+ *
+ * getSchoolId returns a school identifier given one of ours.
+ * getXorgId returns a X.org identifier given a school identifier.
+ */
+ public static function getSchoolId($xorgId)
+ {
+ if (!preg_match('/^[0-9]{8}$/', $xorgId)) {
+ return null;
+ }
+
+ $year = intval(substr($xorgId, 0, 4));
+ $rank = intval(substr($xorgId, 5, 3));
+ if ($year < 1996) {
+ return null;
+ } elseif ($year < 2000) {
+ $year = intval(substr(1900 - $year, 1, 3));
+ return sprintf('%02u0%03u', $year, $rank);
+ } else {
+ $year = intval(substr(1900 - $year, 1, 3));
+ return sprintf('%03u%03u', $year, $rank);
+ }
+ }
+
+ public static function getXorgId($schoolId)
+ {
+ if (!preg_match('/^[0-9]{6}$/', $schoolId)) {
+ return null;
+ }
+
+ $year = intval(substr($schoolId, 0, 3));
+ $rank = intval(substr($schoolId, 3, 3));
+
+ if ($year > 200) {
+ $year /= 10;
+ }
+ if ($year < 96) {
+ return null;
+ } else {
+ return sprintf('%04u%04u', 1900 + $year, $rank);
+ }
+ }
+}
+
+
+/** Iterator over a set of Profiles
+ */
+class ProfileIterator implements PlIterator
+{
+ private $iterator = null;
+ private $fields;
+ private $visibility;
+
+ public function __construct(PlIterator $it, array $pids, $fields = 0x0000, ProfileVisibility $visibility = null)
+ {
+ require_once 'profilefields.inc.php';
+
+ if ($visibility == null) {
+ $visibility = new ProfileVisibility();
+ }
+
+ $this->fields = $fields;
+ $this->visibility = $visibility;
+
+ $subits = array();
+ $callbacks = array();
+
+ $subits[0] = $it;
+ $callbacks[0] = PlIteratorUtils::arrayValueCallback('pid');
+ $cb = PlIteratorUtils::objectPropertyCallback('pid');
+
+ if ($fields & Profile::FETCH_ADDRESSES) {
+ $callbacks[Profile::FETCH_ADDRESSES] = $cb;
+ $subits[Profile::FETCH_ADDRESSES] = new ProfileFieldIterator('ProfileAddresses', $pids, $visibility);
+ }
+
+ if ($fields & Profile::FETCH_CORPS) {
+ $callbacks[Profile::FETCH_CORPS] = $cb;
+ $subits[Profile::FETCH_CORPS] = new ProfileFieldIterator('ProfileCorps', $pids, $visibility);
+ }
+
+ if ($fields & Profile::FETCH_EDU) {
+ $callbacks[Profile::FETCH_EDU] = $cb;
+ $subits[Profile::FETCH_EDU] = new ProfileFieldIterator('ProfileEducation', $pids, $visibility);
+ }
+
+ if ($fields & Profile::FETCH_JOBS) {
+ $callbacks[Profile::FETCH_JOBS] = $cb;
+ $subits[Profile::FETCH_JOBS] = new ProfileFieldIterator('ProfileJobs', $pids, $visibility);
+ }
+
+ if ($fields & Profile::FETCH_MEDALS) {
+ $callbacks[Profile::FETCH_MEDALS] = $cb;
+ $subits[Profile::FETCH_MEDALS] = new ProfileFieldIterator('ProfileMedals', $pids, $visibility);
+ }
+
+ if ($fields & Profile::FETCH_NETWORKING) {
+ $callbacks[Profile::FETCH_NETWORKING] = $cb;
+ $subits[Profile::FETCH_NETWORKING] = new ProfileFieldIterator('ProfileNetworking', $pids, $visibility);
+ }
+
+ if ($fields & Profile::FETCH_PHONES) {
+ $callbacks[Profile::FETCH_PHONES] = $cb;
+ $subits[Profile::FETCH_PHONES] = new ProfileFieldIterator('ProfilePhones', $pids, $visibility);
+ }
+
+ $this->iterator = PlIteratorUtils::parallelIterator($subits, $callbacks, 0);
+ }
+
+ private function hasData($field, $vals)
+ {
+ return ($this->fields & $field) && ($vals[$field] != null);
+ }
+
+ private function fillProfile(array $vals)
+ {
+ $pf = Profile::get($vals[0]);
+ $pf->setVisibilityLevel($this->visibility->level());
+ $pf->setFetchedFields($this->fields);
+
+ if ($this->hasData(Profile::FETCH_PHONES, $vals)) {
+ $pf->setPhones($vals[Profile::FETCH_PHONES]);
+ }
+ if ($this->hasData(Profile::FETCH_ADDRESSES, $vals)) {
+ $pf->setAddresses($vals[Profile::FETCH_ADDRESSES]);
+ }
+ if ($this->hasData(Profile::FETCH_JOBS, $vals)) {
+ $pf->setJobs($vals[Profile::FETCH_JOBS]);
+ }
+
+ if ($this->hasData(Profile::FETCH_CORPS, $vals)) {
+ $pf->setCorps($vals[Profile::FETCH_CORPS]);
+ }
+ if ($this->hasData(Profile::FETCH_EDU, $vals)) {
+ $pf->setEducations($vals[Profile::FETCH_EDU]);
+ }
+ if ($this->hasData(Profile::FETCH_MEDALS, $vals)) {
+ $pf->setMedals($vals[Profile::FETCH_MEDALS]);
+ }
+ if ($this->hasData(Profile::FETCH_NETWORKING, $vals)) {
+ $pf->setNetworking($vals[Profile::FETCH_NETWORKING]);
+ }
+
+ return $pf;
+ }
+
+ public function next()
+ {
+ $vals = $this->iterator->next();
+ if ($vals == null) {
+ return null;
+ }
+ return $this->fillProfile($vals);
+ }
+
+ public function first()
+ {
+ return $this->iterator->first();
+ }
+
+ public function last()
+ {
+ return $this->iterator->last();
+ }
+
+ public function total()
+ {
+ return $this->iterator->total();
+ }