Skip to content

Commit

Permalink
JSON format for api/pc makes more sense
Browse files Browse the repository at this point in the history
And do not show emails to non-PC users.
  • Loading branch information
kohler committed Sep 17, 2024
1 parent c1feff3 commit b8a3ce5
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 124 deletions.
3 changes: 2 additions & 1 deletion etc/apifunctions.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@
{
"name": "pc", "get": true,
"function": "PaperPC_API::pc_api",
"response": "pc"
"parameters": "?ui",
"response": "pc ?tags ?sort ?p"
},
{
"name": "requestreview", "post": true, "paper": true,
Expand Down
123 changes: 74 additions & 49 deletions scripts/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -8079,12 +8079,31 @@ demand_load.make = function (executor) {
};

demand_load.pc = demand_load.make(function (resolve, reject) {
$.get(hoturl("api/pc", {p: siteinfo.paperid}), null, function (v) {
var pc = v && v.ok && v.pc;
$.get(hoturl("api/pc", {p: siteinfo.paperid, ui: 1}), null, function (v) {
const pc = v && v.ok ? v : null;
(pc ? resolve : reject)(pc);
});
});

demand_load.pc_map = demand_load.make(function (resolve) {
demand_load.pc().then(function (v) {
v.umap = {};
v.pc_uids = [];
for (const u of v.pc) {
if (u.uid) {
v.umap[u.uid] = u;
v.pc_uids.push(u.uid);
}
}
for (const pid in v.p || {}) {
for (const u of v.p[pid].extrev || []) {
u.uid && (v.umap[u.uid] = u);
}
}
resolve(v);
});
});

demand_load.search_completion = demand_load.make(function (resolve) {
$.get(hoturl("api/searchcompletion"), null, function (v) {
var sc = (v && v.searchcompletion) || [],
Expand Down Expand Up @@ -8819,7 +8838,7 @@ hotcrp.suggest.add_builder("pc-tags", function (elt) {
if (x && (m = x[0].match(/(?:^|\s)(#?)([^#\s]*)$/))) {
n = x[1].match(/^(\S*)/);
return demand_load.pc().then(function (pc) {
return make_suggestions(m[2], n[1])(pc.__tags__ || []);
return make_suggestions(m[2], n[1])(pc.tags || []);
});
}
});
Expand Down Expand Up @@ -10891,12 +10910,12 @@ function pattrnear(e, attr) {

function render_allpref() {
var pctr = this;
demand_load.pc().then(function (pcs) {
demand_load.pc_map().then(function (pcs) {
var allpref = pattrnear(pctr, "data-allpref") || "",
atomre = /(\d+)([PT])(\S+)/g, t = [], m, pref, ul, i, e, u;
while ((m = atomre.exec(allpref)) !== null) {
pref = parseInt(m[3]);
t.push([m[2] === "P" ? pref : 0, pref, t.length, pcs[m[1]], m[2]]);
t.push([m[2] === "P" ? pref : 0, pref, t.length, pcs.umap[m[1]], m[2]]);
}
if (t.length === 0) {
pctr.closest(".ple, .pl").replaceChildren();
Expand Down Expand Up @@ -13786,66 +13805,72 @@ $(".js-radio-focus").on("click keypress", "input, select", function () {
function populate_pcselector() {
const self = this;
removeClass(self, "need-pcselector");
let optids = self.getAttribute("data-pcselector-options") || "*";
optids = optids.startsWith("[") ? JSON.parse(optids) : optids.split(/[\s,]+/);
let selected, selindex = -1;
let options = self.getAttribute("data-pcselector-options") || "*";
options = options.startsWith("[") ? JSON.parse(options) : options.split(/[\s,]+/);
let selected;
if (self.hasAttribute("data-pcselector-selected")) {
selected = self.getAttribute("data-pcselector-selected");
} else {
selected = self.getAttribute("data-default-value");
}

demand_load.pc().then(function (pcs) {
let last_first = pcs.__sort__ === "last", used = {}, opt, p, curgroup = self;

for (let i = 0; i < optids.length; ++i) {
let cid = optids[i];
if (cid === "" || cid === "*") {
optids.splice.apply(optids, [i + 1, 0].concat(pcs.__order__));
} else if (cid === "assignable") {
optids.splice.apply(optids, [i + 1, 0].concat(pcs.__assignable__[siteinfo.paperid] || []));
} else if (cid === "selected") {
if (selected != null)
optids.splice.apply(optids, [i + 1, 0, selected]);
} else if (cid === "extrev") {
let extrevs = pcs.__extrev__ ? pcs.__extrev__[siteinfo.paperid] : null;
if (extrevs && extrevs.length) {
optids.splice.apply(optids, [i + 1, 0].concat(extrevs));
optids.splice(i + 1 + extrevs.length, 0, "endgroup");
curgroup = document.createElement("optgroup");
curgroup.setAttribute("label", "External reviewers");
self.appendChild(curgroup);
demand_load.pc_map().then(function (pcs) {
const last_first = pcs.sort === "last", used = {}, optids = [];
let selindex = -1, noptions = 0;

for (const opt of options) {
let want, mygroup = self;
if (opt === "" || opt === "*") {
want = pcs.pc_uids;
} else if (opt === "assignable") {
want = pcs.p[siteinfo.paperid].assignable;
} else if (opt === "selected") {
want = selected != null ? [selected] : [];
} else if (opt === "extrev") {
mygroup = document.createElement("optgroup");
mygroup.setAttribute("label", "External reviewers");
want = [];
for (const u of pcs.p[siteinfo.paperid].extrev || []) {
want.push(u.uid);
}
} else if (cid === "endgroup") {
curgroup = self;
} else {
cid = +cid;
want = [+opt || 0];
}
for (const uid of want) {
if (used[uid]) {
continue;
}
const p = pcs.umap[uid];
let email, name;
if (!cid) {
email = "none";
name = optids[i];
if (name === "" || name === 0 || name === "0")
name = "None";
} else if ((p = pcs[cid])
|| (pcs.__other__ && (p = pcs.__other__[cid]))) {
if (p) {
email = p.email;
name = p.name;
if (last_first && p.lastpos) {
var nameend = p.emailpos ? p.emailpos - 1 : name.length;
const nameend = p.emailpos ? p.emailpos - 1 : name.length;
name = name.substring(p.lastpos, nameend) + ", " + name.substring(0, p.lastpos - 1) + name.substring(nameend);
}
} else if (!uid) {
email = "none";
if (opt === "" || opt === 0 || opt === "0") {
name = "None";
} else {
name = opt;
}
} else {
continue;
}
if (!used[email]) {
used[email] = true;
opt = document.createElement("option");
opt.setAttribute("value", email);
opt.text = name;
curgroup.appendChild(opt);
if (email === selected || (email !== "none" && cid == selected))
selindex = self.options.length - 1;
used[uid] = true;
const e = document.createElement("option");
e.setAttribute("value", email);
e.text = name;
mygroup.appendChild(e);
if (email === selected || (uid && uid == selected)) {
selindex = noptions;
}
++noptions;
}
if (mygroup !== self && mygroup.firstChild) {
self.appendChild(mygroup);
}
}

Expand All @@ -13854,11 +13879,11 @@ function populate_pcselector() {
selindex = 0;
} else {
opt = document.createElement("option");
const p = pcs[selected];
const p = pcs.umap[selected];
opt.setAttribute("value", p ? p.email : selected);
opt.text = p ? p.name + " (not assignable)" : "[removed from PC]";
self.appendChild(opt);
selindex = self.options.length - 1;
selindex = noptions;
}
}
self.selectedIndex = selindex;
Expand Down
7 changes: 5 additions & 2 deletions src/api/api_paperpc.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ static function pc_api(Contact $user, Qrequest $qreq) {
if (!$user->can_view_pc()) {
return JsonResult::make_permission_error();
}
$pc = $user->conf->hotcrp_pc_json($user);
return ["ok" => true, "pc" => $pc];
$jr = new JsonResult($user->conf->hotcrp_pc_json($user, $qreq->ui ? Conf::PCJM_UI : Conf::PCJM_DEFAULT));
if ($qreq->ui) {
$jr->set_pretty_print(false);
}
return $jr;
}
}
122 changes: 67 additions & 55 deletions src/conference.php
Original file line number Diff line number Diff line change
Expand Up @@ -4983,97 +4983,109 @@ static function git_status() {
return count($args) == 2 ? $args : null;
}

/** @param Contact $viewer
* @param Contact $user */
private function pc_json_item($viewer, $user) {
$name = $viewer->name_text_for($user);
$j = (object) [
"name" => $name !== "" ? $name : $user->email,
"email" => $user->email
];
if (($color_classes = $user->viewable_color_classes($viewer))) {
$j->color_classes = $color_classes;
const PCJ_EMAIL = 0x1;
const PCJ_AFFILIATION = 0x2;
const PCJ_ORCID = 0x4;
const PCJ_UID = 0x8;
const PCJM_DEFAULT = 0x7;
const PCJM_UI = 0x9;

/** @param ?Contact $viewer
* @param Contact $user
* @param int $flags
* @return object */
private function user_json($viewer, $user, $flags) {
$name = $viewer ? $viewer->name_text_for($user) : Text::nameo($user, NAME_P);
if ($name === "" && ($flags & self::PCJ_EMAIL) !== 0) {
$name = $user->email;
}
$j = (object) ["name" => $name];
if (($flags & self::PCJ_EMAIL) !== 0) {
$j->email = $user->email;
}
if ($this->sort_by_last && $user->lastName !== "") {
self::pc_json_sort_by_last($j, $user);
if (strlen($user->lastName) !== strlen($name)) {
$j->lastpos = UnicodeHelper::utf16_strlen($user->firstName) + 1;
}
if (($user->nameAmbiguous ?? false) && $name !== "" && ($j->email ?? "") !== "") {
$j->emailpos = UnicodeHelper::utf16_strlen($name) + 1;
}
}
if (($flags & self::PCJ_AFFILIATION) !== 0) {
$j->affiliation = $user->affiliation;
}
if (($flags & self::PCJ_ORCID) !== 0 && ($orcid = $user->orcid())) {
$j->orcid = $orcid;
}
if (($flags & self::PCJ_UID) !== 0) {
$j->uid = $user->contactId;
}
return $j;
}

/** @param Contact $viewer
* @param ReviewInfo $rrow
* @return stdClass */
private function pc_json_reviewer_item($viewer, $rrow) {
$user = $rrow->reviewer();
$j = (object) [
"name" => Text::nameo($user, NAME_P),
"email" => $user->email
];
if ($this->sort_by_last && $user->lastName !== "") {
self::pc_json_sort_by_last($j, $user);
* @param Contact $user
* @param int $flags
* @return object */
private function pc_json_item($viewer, $user, $flags) {
$j = $this->user_json($viewer, $user, $flags);
if (($color_classes = $user->viewable_color_classes($viewer))) {
$j->color_classes = $color_classes;
}
if (($user->roles & Contact::ROLE_CHAIR) !== 0) {
$j->roles = "chair";
}
return $j;
}

/** @param Contact|ReviewInfo $r */
static private function pc_json_sort_by_last($j, $r) {
if (strlen($r->lastName) !== strlen($j->name)) {
$j->lastpos = UnicodeHelper::utf16_strlen($r->firstName) + 1;
}
if (($r->nameAmbiguous ?? false) && $j->name !== "" && $r->email !== "") {
$j->emailpos = UnicodeHelper::utf16_strlen($j->name) + 1;
/** @param int $flags
* @return array<string,mixed> */
function hotcrp_pc_json(Contact $viewer, $flags) {
if (!$viewer->isPC) {
$flags &= ~(self::PCJ_EMAIL | self::PCJ_UID);
}
}

/** @return array<string,mixed> */
function hotcrp_pc_json(Contact $viewer) {
$hpcj = $list = $otherj = [];
$pcj = $otherj = [];
foreach ($this->pc_members() as $pcm) {
$hpcj[$pcm->contactId] = $this->pc_json_item($viewer, $pcm);
$list[] = $pcm->contactId;
$pcj[] = $this->pc_json_item($viewer, $pcm, $flags);
}
$hpcj["__order__"] = $list;
$rj = ["ok" => true, "pc" => $pcj];
if ($this->sort_by_last) {
$hpcj["__sort__"] = "last";
$rj["sort"] = "last";
}
if ($viewer->can_view_user_tags()) {
$hpcj["__tags__"] = $this->viewable_user_tags($viewer);
$rj["tags"] = $this->viewable_user_tags($viewer);
}
if ($this->paper
&& ($viewer->privChair || $viewer->allow_administer($this->paper))) {
$list = [];
if ($this->paper && $viewer->allow_administer($this->paper)) {
$assignable = $erlist = [];
foreach ($this->pc_members() as $pcm) {
if ($pcm->pc_assignable($this->paper)) {
$list[] = $pcm->contactId;
$assignable[] = $pcm->contactId;
}
}
$hpcj["__assignable__"] = [$this->paper->paperId => $list];
if ($this->setting("extrev_shepherd")) {
$this->paper->ensure_reviewer_names();
$erlist = [];
foreach ($this->paper->reviews_as_display() as $rrow) {
if ($rrow->reviewType == REVIEW_EXTERNAL
if ($rrow->reviewType === REVIEW_EXTERNAL
&& !$rrow->reviewToken
&& !in_array($rrow->contactId, $erlist)) {
$otherj[$rrow->contactId] = $this->pc_json_reviewer_item($viewer, $rrow);
$erlist[] = $rrow->contactId;
$erlist[] = $this->user_json(null, $rrow->reviewer(), $flags);
}
}
if (!empty($erlist)) {
$hpcj["__extrev__"] = [$this->paper->paperId => $erlist];
}
}
$pj = ["assignable" => $assignable];
if (!empty($erlist)) {
$pj["extrev"] = $erlist;
}
$rj["p"] = [$this->paper->paperId => $pj];
}
if (!empty($otherj)) {
$hpcj["__other__"] = $otherj;
}
return $hpcj;
return (object) $rj;
}

function stash_hotcrp_pc(Contact $viewer, $always = false) {
if (($always || !$this->opt("largePC"))
&& $viewer->can_view_pc()
&& Ht::mark_stash("hotcrp_pc")) {
Ht::stash_script("hotcrp.demand_load.pc(" . json_encode_browser($this->hotcrp_pc_json($viewer)) . ");");
Ht::stash_script("hotcrp.demand_load.pc(" . json_encode_browser($this->hotcrp_pc_json($viewer, self::PCJM_UI)) . ");");
}
}

Expand Down
15 changes: 7 additions & 8 deletions src/contactlist.php
Original file line number Diff line number Diff line change
Expand Up @@ -837,16 +837,15 @@ function content($fieldId, $row) {
}
return $t;
case self::FIELD_EMAIL:
if ($this->user->isPC) {
$e = htmlspecialchars($row->email);
if (strpos($row->email, "@") === false) {
return $e;
} else {
return "<a href=\"mailto:$e\" class=\"q\">$e</a>";
}
} else {
if (!$this->user->isPC) {
return "";
}
$e = htmlspecialchars($row->email);
if (strpos($row->email, "@") === false) {
return $e;
} else {
return "<a href=\"mailto:{$e}\" class=\"q\">{$e}</a>";
}
case self::FIELD_AFFILIATION:
case self::FIELD_AFFILIATION_ROW:
return htmlspecialchars($row->affiliation);
Expand Down
Loading

0 comments on commit b8a3ce5

Please sign in to comment.