function pcMembers() { global $Conf, $Opt, $PcMembersCache; if (!@$PcMembersCache || $Conf->setting("pc") <= 0 || $PcMembersCache[0] < $Conf->setting("pc") || $PcMembersCache[1] != @$Opt["sortByLastName"]) { $pc = array(); $result = Dbl::q("select firstName, lastName, affiliation, email, contactId, roles, contactTags, disabled from ContactInfo where (roles&" . Contact::ROLE_PC . ")!=0"); $by_name_text = array(); $pctags = array("pc" => "pc"); while ($result && ($row = Contact::fetch($result))) { $pc[$row->contactId] = $row; if ($row->firstName || $row->lastName) { $name_text = Text::name_text($row); if (isset($by_name_text[$name_text])) { $row->nameAmbiguous = $by_name_text[$name_text]->nameAmbiguous = true; } $by_name_text[$name_text] = $row; } if ($row->contactTags) { foreach (explode(" ", $row->contactTags) as $t) { list($tag, $value) = TagInfo::split_index($t); if ($tag) { $pctags[strtolower($tag)] = $tag; } } } } uasort($pc, "Contact::compare"); ksort($pctags); $order = 0; foreach ($pc as $row) { $row->sort_position = $order; ++$order; } $PcMembersCache = array($Conf->setting("pc"), @$Opt["sortByLastName"], $pc, $pctags); } return $PcMembersCache[2]; }
function save_json($cj, $actor, $send) { global $Conf, $Me, $Now; $inserting = !$this->contactId; $old_roles = $this->roles; $old_email = $this->email; $different_email = strtolower($cj->email) !== strtolower((string) $old_email); $cu = new Contact_Update($inserting, $different_email); $aupapers = null; if ($different_email) { $aupapers = self::email_authored_papers($cj->email, $cj); } // check whether this user is changing themselves $changing_other = false; if (self::contactdb() && $Me && (strcasecmp($this->email, $Me->email) != 0 || $Me->is_actas_user())) { $changing_other = true; } // Main fields foreach (array("firstName", "lastName", "email", "affiliation", "collaborators", "preferredEmail", "country") as $k) { if (isset($cj->{$k})) { $this->_save_assign_field($k, $cj->{$k}, $cu); } } if (isset($cj->phone)) { $this->_save_assign_field("voicePhoneNumber", $cj->phone, $cu); } $this->_save_assign_field("unaccentedName", Text::unaccented_name($this->firstName, $this->lastName), $cu); self::set_sorter($this); // Disabled $disabled = $this->disabled ? 1 : 0; if (isset($cj->disabled)) { $disabled = $cj->disabled ? 1 : 0; } if (($this->disabled ? 1 : 0) !== $disabled || !$this->contactId) { $cu->qv["disabled"] = $this->disabled = $disabled; } // Data $old_datastr = $this->data_str(); $data = (object) array(); foreach (array("address", "city", "state", "zip") as $k) { if (isset($cj->{$k}) && ($x = $cj->{$k})) { while (is_array($x) && $x[count($x) - 1] === "") { array_pop($x); } $data->{$k} = $x ?: null; } } $this->merge_data($data); $datastr = $this->data_str(); if ($datastr !== $old_datastr) { $cu->qv["data"] = $datastr; } // Changes to the above fields also change the updateTime. if (count($cu->qv)) { $cu->qv["updateTime"] = $this->updateTime = $Now; } // Follow if (isset($cj->follow)) { $w = 0; if (get($cj->follow, "reviews")) { $w |= WATCH_COMMENT; } if (get($cj->follow, "allreviews")) { $w |= WATCH_ALLCOMMENTS; } if (get($cj->follow, "allfinal")) { $w |= WATCHTYPE_FINAL_SUBMIT << WATCHSHIFT_ALL; } $this->_save_assign_field("defaultWatch", $w, $cu); } // Tags if (isset($cj->tags)) { $tags = array(); foreach ($cj->tags as $t) { list($tag, $value) = TagInfo::split_index($t); if (strcasecmp($tag, "pc") != 0) { $tags[$tag] = $tag . "#" . ($value ?: 0); } } ksort($tags); $t = count($tags) ? " " . join(" ", $tags) . " " : ""; $this->_save_assign_field("contactTags", $t, $cu); } // If inserting, set initial password and creation time if ($inserting) { $cu->qv["creationTime"] = $this->creationTime = $Now; $this->_create_password(self::contactdb_find_by_email($this->email), $cu); } // Initial save if (count($cu->qv)) { // always true if $inserting $q = ($inserting ? "insert into" : "update") . " ContactInfo set " . join("=?, ", array_keys($cu->qv)) . "=?" . ($inserting ? "" : " where contactId={$this->contactId}"); if (!($result = Dbl::qe_apply($Conf->dblink, $q, array_values($cu->qv)))) { return $result; } if ($inserting) { $this->contactId = $this->cid = (int) $result->insert_id; } Dbl::free($result); } // Topics if (isset($cj->topics)) { $tf = array(); foreach ($cj->topics as $k => $v) { $tf[] = "({$this->contactId},{$k},{$v})"; } $Conf->qe("delete from TopicInterest where contactId={$this->contactId}"); if (count($tf)) { $Conf->qe("insert into TopicInterest (contactId,topicId,interest) values " . join(",", $tf)); } } // Roles $roles = 0; if (isset($cj->roles)) { $roles = self::parse_roles_json($cj->roles); if ($roles !== $old_roles) { $this->save_roles($roles, $actor); } } // Update authorship if ($aupapers) { $this->save_authored_papers($aupapers); } // Update contact database $cdbu = $this->contactDbId ? $this : $this->contactdb_user_; if ($different_email) { $cdbu = null; } if (($cdb = self::contactdb()) && (!$cdbu || count($cu->cdb_uqv))) { $qv = []; if (!$cdbu) { $q = "insert into ContactInfo set firstName=?, lastName=?, email=?, affiliation=?, country=?, collaborators=?"; $qv = array($this->firstName, $this->lastName, $this->email, $this->affiliation, $this->country, $this->collaborators); if ($this->password !== "" && ($this->password[0] !== " " || $this->password[1] === "\$")) { $q .= ", password=?"; $qv[] = $this->password; } $q .= " on duplicate key update "; } else { $q = "update ContactInfo set "; } if (count($cu->cdb_uqv) && $changing_other) { $q .= join(", ", array_map(function ($k) { return "{$k}=if(coalesce({$k},'')='',?,{$k})"; }, array_keys($cu->cdb_uqv))); } else { if (count($cu->cdb_uqv)) { $q .= join("=?, ", array_keys($cu->cdb_uqv)) . "=?"; } else { $q .= "firstName=firstName"; } } if (count($cu->cdb_uqv)) { $q .= ", updateTime={$Now}"; } $qv = array_merge($qv, array_values($cu->cdb_uqv)); if ($cdbu) { $q .= " where contactDbId=" . $cdbu->contactDbId; } $result = Dbl::ql_apply($cdb, $q, $qv); Dbl::free($result); $this->contactdb_user_ = false; } // Password if (isset($cj->new_password)) { $this->change_password(get($cj, "old_password"), $cj->new_password, 0); } // Beware PC cache if (($roles | $old_roles) & Contact::ROLE_PCLIKE) { $Conf->invalidateCaches(array("pc" => 1)); } // Mark creation and activity if ($inserting) { if ($send && !$this->disabled) { $this->sendAccountInfo("create", false); } $type = $this->disabled ? "disabled " : ""; if ($Me && $Me->has_email() && $Me->email !== $this->email) { $Conf->log("Created {$type}account ({$Me->email})", $this); } else { $Conf->log("Created {$type}account", $this); } } $actor = $actor ?: $Me; if ($actor && $this->contactId == $actor->contactId) { $this->mark_activity(); } return true; }
private function parse_list($sv, $si, $checkf, $min_idx) { $ts = array(); foreach (preg_split('/\\s+/', $sv->req[$si->name]) as $t) { if ($t !== "" && $this->tagger->check($t, $checkf)) { list($tag, $idx) = TagInfo::split_index($t); if ($min_idx) { $t = $tag . "#" . max($min_idx, (double) $idx); } $ts[$tag] = $t; } else { if ($t !== "") { $sv->set_error($si->name, $si->short_description . ": " . $this->tagger->error_html); } } } return array_values($ts); }
private function normalize($cj, $old_user) { // Errors prevent saving global $Conf, $Me, $Now; // Canonicalize keys foreach (array("preferredEmail" => "preferred_email", "institution" => "affiliation", "voicePhoneNumber" => "phone", "addressLine1" => "address", "zipCode" => "zip", "postal_code" => "zip") as $x => $y) { if (isset($cj->{$x}) && !isset($cj->{$y})) { $cj->{$y} = $cj->{$x}; } } // Stringiness foreach (array("firstName", "lastName", "email", "preferred_email", "affiliation", "phone", "old_password", "new_password", "city", "state", "zip", "country") as $k) { if (isset($cj->{$k}) && !is_string($cj->{$k})) { $this->set_error($k, "Format error [{$k}]"); unset($cj->{$k}); } } // Email if (!get($cj, "email") && $old_user) { $cj->email = $old_user->email; } else { if (!get($cj, "email")) { $this->set_error("email", "Email is required."); } else { if (!isset($this->errf["email"]) && !validate_email($cj->email) && (!$old_user || $old_user->email !== $cj->email)) { $this->set_error("email", "Invalid email address “" . htmlspecialchars($cj->email) . "”."); } } } // ID if (get($cj, "id") === "new") { if (get($cj, "email") && Contact::id_by_email($cj->email)) { $this->set_error("email", "Email address “" . htmlspecialchars($cj->email) . "” is already in use."); $this->errf["email_inuse"] = true; } } else { if (!get($cj, "id") && $old_user && $old_user->contactId) { $cj->id = $old_user->contactId; } if (get($cj, "id") && !is_int($cj->id)) { $this->set_error("id", "Format error [id]"); } if ($old_user && get($cj, "email") && strtolower($old_user->email) !== strtolower($cj->email) && Contact::id_by_email($cj->email)) { $this->set_error("email", "Email address “" . htmlspecialchars($cj->email) . "” is already in use. You may want to <a href=\"" . hoturl("mergeaccounts") . "\">merge these accounts</a>."); } } // Contactdb information if ($old_user && !$old_user->contactId) { if (!isset($cj->firstName) && !isset($cj->lastName)) { $cj->firstName = $old_user->firstName; $cj->lastName = $old_user->lastName; } if (!isset($cj->affiliation)) { $cj->affiliation = $old_user->affiliation; } if (!isset($cj->collaborators)) { $cj->collaborators = $old_user->collaborators; } } // Preferred email if (get($cj, "preferred_email") && !isset($this->errf["preferred_email"]) && !validate_email($cj->preferred_email) && (!$old_user || $old_user->preferredEmail !== $cj->preferred_email)) { $this->set_error("preferred_email", "Invalid email address “" . htmlspecialchars($cj->preferred_email) . "”"); } // Address $address = array(); if (is_array(get($cj, "address"))) { $address = $cj->address; } else { if (is_string(get($cj, "address"))) { $address[] = $cj->address; } else { if (get($cj, "address")) { $this->set_error("address", "Format error [address]"); } } if (is_string(get($cj, "address2"))) { $address[] = $cj->address2; } else { if (is_string(get($cj, "addressLine2"))) { $address[] = $cj->addressLine2; } else { if (get($cj, "address2") || get($cj, "addressLine2")) { $this->set_error("address2", "Format error [address2]"); } } } } foreach ($address as $a) { if (!is_string($a)) { $this->set_error("address", "Format error [address]"); } } if (count($address)) { $cj->address = $address; } // Collaborators if (is_array(get($cj, "collaborators"))) { foreach ($cj->collaborators as $c) { if (!is_string($c)) { $this->set_error("collaborators", "Format error [collaborators]"); } } } if (is_array(get($cj, "collaborators")) && !isset($this->errf["collaborators"])) { $cj->collaborators = join("\n", $cj->collaborators); } if (get($cj, "collaborators") && !is_string($cj->collaborators) && !isset($this->errf["collaborators"])) { $this->set_error("collaborators", "Format error [collaborators]"); } // Disabled if (isset($cj->disabled)) { if (($x = friendly_boolean($cj->disabled)) !== null) { $cj->disabled = $x; } else { $this->set_error("disabled", "Format error [disabled]"); } } // Follow if (isset($cj->follow)) { $cj->follow = $this->make_keyed_object($cj->follow, "follow"); $cj->bad_follow = array(); foreach ((array) $cj->follow as $k => $v) { if ($v && $k !== "reviews" && $k !== "allreviews" && $k !== "allfinal") { $cj->bad_follow[] = $k; } } } // Roles if (isset($cj->roles)) { $cj->roles = $this->make_keyed_object($cj->roles, "roles"); $cj->bad_roles = array(); foreach ((array) $cj->roles as $k => $v) { if ($v && $k !== "pc" && $k !== "chair" && $k !== "sysadmin" && $k !== "no") { $cj->bad_roles[] = $k; } } if ($this->no_deprivilege_self && $Me && $old_user && $old_user->contactId == $Me->contactId && Contact::parse_roles_json($cj->roles) < $Me->roles) { unset($cj->roles); $this->set_warning("roles", "Ignoring request to drop your privileges."); } } // Tags if (isset($cj->tags)) { $cj->tags = $this->make_tags_array($cj->tags, "tags"); } if (isset($cj->add_tags) || isset($cj->remove_tags)) { // collect old tags as map by base if (!isset($cj->tags) && $old_user) { $cj->tags = preg_split("/[\\s,]+/", $old_user->contactTags); } else { if (!isset($cj->tags)) { $cj->tags = array(); } } $old_tags = array(); foreach ($cj->tags as $t) { if ($t !== "") { list($tag, $index) = TagInfo::split_index($t); $old_tags[$tag] = $index; } } // process removals, then additions foreach ($this->make_tags_array(get($cj, "remove_tags"), "remove_tags") as $t) { list($tag, $index) = TagInfo::split_index($t); if ($index === false || get($old_tags, $tag) == $index) { unset($old_tags[$tag]); } } foreach ($this->make_tags_array($cj->add_tags, "add_tags") as $t) { list($tag, $index) = TagInfo::split_index($t); $old_tags[$tag] = $index; } // collect results $cj->tags = array(); foreach ($old_tags as $tag => $index) { $cj->tags[] = $tag . "#" . (double) $index; } } // Topics if (isset($cj->topics)) { $topics = $this->make_keyed_object($cj->topics, "topics"); $topic_map = $Conf->topic_map(); $cj->topics = (object) array(); $cj->bad_topics = array(); foreach ((array) $topics as $k => $v) { if (get($topic_map, $k)) { /* OK */ } else { if (($x = array_search($k, $topic_map, true)) !== false) { $k = $x; } else { $cj->bad_topics[] = $k; continue; } } if ($v === "mlow" || $v === "medium-low") { $v = -1; } else { if ($v === true || $v === "mhigh" || $v === "medium-high") { $v = 2; } else { if ($v === "low") { $v = -2; } else { if ($v === "high") { $v = 4; } else { if ($v === "medium" || $v === "none" || $v === false) { $v = 0; } else { if (is_numeric($v)) { $v = (int) $v; } else { $this->set_error("topics", "Topic interest format error"); continue; } } } } } } $k = (string) $k; $cj->topics->{$k} = $v; } } }
function do_tags() { global $Conf, $Me, $papersel; // check tags $tagger = new Tagger($Me); $t1 = array(); $errors = array(); foreach (preg_split('/[\\s,]+/', (string) @$_REQUEST["tag"]) as $t) { if ($t === "") { /* nada */ } else { if (!($t = $tagger->check($t, Tagger::NOPRIVATE))) { $errors[] = $tagger->error_html; } else { if (TagInfo::base($t) === "pc") { $errors[] = "The “pc” user tag is set automatically for all PC members."; } else { $t1[] = $t; } } } } if (count($errors)) { return Conf::msg_error(join("<br>", $errors)); } else { if (!count($t1)) { return $Conf->warnMsg("Nothing to do."); } } // modify database Dbl::qe("lock tables ContactInfo write"); Conf::$no_invalidate_caches = true; $users = array(); if ($_REQUEST["tagtype"] === "s") { // erase existing tags $likes = array(); $removes = array(); foreach ($t1 as $t) { list($tag, $index) = TagInfo::split_index($t); $removes[] = $t; $likes[] = "contactTags like " . Dbl::utf8ci("'% " . sqlq_for_like($tag) . "#%'"); } foreach (Dbl::fetch_first_columns(Dbl::qe("select contactId from ContactInfo where " . join(" or ", $likes))) as $cid) { $users[(int) $cid] = (object) array("id" => (int) $cid, "add_tags" => [], "remove_tags" => $removes); } } // account for request $key = $_REQUEST["tagtype"] === "d" ? "remove_tags" : "add_tags"; foreach ($papersel as $cid) { if (!isset($users[(int) $cid])) { $users[(int) $cid] = (object) array("id" => (int) $cid, "add_tags" => [], "remove_tags" => []); } $users[(int) $cid]->{$key} = array_merge($users[(int) $cid]->{$key}, $t1); } // apply modifications foreach ($users as $cid => $cj) { $us = new UserStatus(array("send_email" => false)); if (!$us->save($cj)) { $errors = array_merge($errors, $us->error_messages()); } } Dbl::qe("unlock tables"); Conf::$no_invalidate_caches = false; $Conf->invalidateCaches(["pc" => true]); // report if (!count($errors)) { $Conf->confirmMsg("Tags saved."); redirectSelf(array("tagact" => null, "tag" => null)); } else { Conf::msg_error(join("<br>", $errors)); } }
function pc_members() { if ($this->_pc_members_cache === null) { $pc = $pca = array(); $result = $this->q("select firstName, lastName, affiliation, email, contactId, roles, contactTags, disabled from ContactInfo where roles!=0 and (roles&" . Contact::ROLE_PCLIKE . ")!=0"); $by_name_text = $by_first_text = []; $this->_pc_tags_cache = ["pc" => "pc"]; while ($result && ($row = Contact::fetch($result, $this))) { $pca[$row->contactId] = $row; if ($row->roles & Contact::ROLE_PC) { $pc[$row->contactId] = $row; } if ($row->firstName || $row->lastName) { $name_text = Text::name_text($row); if (isset($by_name_text[$name_text])) { $row->nameAmbiguous = $by_name_text[$name_text]->nameAmbiguous = true; } $by_name_text[$name_text] = $row; } if ($row->firstName) { if (isset($by_first_text[$row->firstName])) { $row->firstNameAmbiguous = $by_first_text[$row->firstName]->firstNameAmbiguous = true; } $by_first_text[$row->firstName] = $row; } if ($row->contactTags) { foreach (explode(" ", $row->contactTags) as $t) { list($tag, $value) = TagInfo::split_index($t); if ($tag) { $this->_pc_tags_cache[strtolower($tag)] = $tag; } } } } Dbl::free($result); uasort($pc, "Contact::compare"); $order = 0; foreach ($pc as $row) { $row->sort_position = $order; ++$order; } $this->_pc_members_cache = $pc; uasort($pca, "Contact::compare"); $this->_pc_members_and_admins_cache = $pca; ksort($this->_pc_tags_cache); } return $this->_pc_members_cache; }