+ if (!($login instanceof PlUser)) {
+ $user = User::getSilent($login);
+ if ($user && $user->hasProfile()) {
+ return $user->profile();
+ }
+ }
+ return null;
+ }
+ }
+
+ public static function iterOverUIDs($uids, $respect_order = true, $fields = 0x0000, $visibility = null)
+ {
+ return self::iterOverPIDs(self::getPIDsFromUIDs($uids), $respect_order, $fields, $visibility);
+ }
+
+ public static function iterOverPIDs($pids, $respect_order = true, $fields = 0x0000, $visibility = null)
+ {
+ return self::fetchProfileData($pids, $respect_order, $fields, $visibility);
+ }
+
+ /** Return profiles for the list of pids.
+ */
+ public static function getBulkProfilesWithPIDs(array $pids, $fields = self::FETCH_ADDRESSES, $visibility = null)
+ {
+ if (count($pids) == 0) {
+ return array();
+ }
+ $it = self::iterOverPIDs($pids, true, $fields, $visibility);
+ $profiles = array();
+ while ($p = $it->next()) {
+ $profiles[$p->id()] = $p;
+ }
+ return $profiles;
+ }
+
+ /** Return profiles for uids.
+ */
+ public static function getBulkProfilesWithUIDS(array $uids, $fields = 0x000, $visibility = null)
+ {
+ if (count($uids) == 0) {
+ return array();
+ }
+ return self::getBulkProfilesWithPIDs(self::getPIDsFromUIDs($uids), $fields, $visibility);
+ }
+
+ public static function isDisplayName($name)
+ {
+ return $name == self::DN_FULL || $name == self::DN_DISPLAY
+ || $name == self::DN_YOURSELF || $name == self::DN_DIRECTORY
+ || $name == self::DN_PRIVATE || $name == self::DN_PUBLIC
+ || $name == self::DN_SHORT || $name == self::DN_SORT;
+ }
+
+ public static function getNameTypeId($type, $for_sql = false)
+ {
+ if (!S::has('name_types')) {
+ $table = XDB::fetchAllAssoc('type', 'SELECT id, type
+ FROM profile_name_enum');
+ S::set('name_types', $table);
+ } else {
+ $table = S::v('name_types');
+ }
+ if ($for_sql) {
+ return XDB::escape($table[$type]);
+ } else {
+ return $table[$type];
+ }
+ }
+
+ 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);
+
+ foreach ($keys as $i => $key) {
+ 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);
+ }
+ }
+}
+
+class ProfileDataIterator
+{
+ private $iterator = null;
+ private $fields;
+
+ public function __construct(PlIterator $it, array $pids, $fields = 0x0000, $visibility = null)
+ {
+ require_once 'profilefields.inc.php';
+ $visibility = Profile::getVisibilityContext($visibility);
+ $this->fields = $fields;
+
+ $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);
+ }
+
+ if ($fields & Profile::FETCH_PHOTO) {
+ $callbacks[Profile::FETCH_PHOTO] = $cb;
+ $subits[Profile::FETCH_PHOTO] = new ProfileFieldIterator('ProfilePhoto', $pids, $visibility);
+ }
+
+ $this->iterator = PlIteratorUtils::parallelIterator($subits, $callbacks, 0);
+ }
+
+ private function consolidateFields(array $pf)
+ {
+ if ($this->fields & Profile::FETCH_PHONES) {
+ $phones = $pf[Profile::FETCH_PHONES];
+
+ if ($this->fields & Profile::FETCH_ADDRESSES && $pf[Profile::FETCH_ADDRESSES] != null) {
+ $pf[Profile::FETCH_ADDRESSES]->addPhones($phones);