public function delete($capdata) { assert(!$capdata || is_string($capdata->salt)); if ($capdata) { Dbl::ql($this->dblink, "delete from Capability where salt=?", $capdata->salt); } }
function run(Contact $user, $qreq, $ssel) { global $Conf; $o = cvtint($qreq->decision); $decision_map = $Conf->decision_map(); if ($o === null || !isset($decision_map[$o])) { return Conf::msg_error("Bad decision value."); } $result = Dbl::qe_raw($Conf->paperQuery($user, array("paperId" => $ssel->selection()))); $success = $fails = array(); while ($prow = PaperInfo::fetch($result, $user)) { if ($user->can_set_decision($prow, true)) { $success[] = $prow->paperId; } else { $fails[] = "#" . $prow->paperId; } } if (count($fails)) { Conf::msg_error("You cannot set paper decisions for " . pluralx($fails, "paper") . " " . commajoin($fails) . "."); } if (count($success)) { Dbl::qe("update Paper set outcome={$o} where paperId ?a", $success); $Conf->update_paperacc_setting($o > 0); redirectSelf(array("atab" => "decide", "decision" => $o)); } }
function save_password($email, $encoded_password, $iscdb = false) { global $Conf, $Now; $dblink = $iscdb ? Contact::contactdb() : $Conf->dblink; Dbl::qe($dblink, "update ContactInfo set password=?, passwordTime=? where email=?", $encoded_password, $Now, $email); ++$Now; }
static function save_review_preferences($prefarray) { global $Conf; $q = array(); foreach ($prefarray as $p) { $q[] = "({$p['0']},{$p['1']},{$p['2']}," . ($p[3] === null ? "NULL" : $p[3]) . ")"; } if (count($q)) { return Dbl::qe_raw("insert into PaperReviewPreference (paperId,contactId,preference,expertise) values " . join(",", $q) . " on duplicate key update preference=values(preference), expertise=values(expertise)"); } return true; }
static function enable($ids, $contact) { global $Conf; $result = $Conf->qe_raw("update ContactInfo set disabled=1 where contactId" . sql_in_numeric_set($ids) . " and password='' and contactId!=" . $contact->contactId); $result = Dbl::qe("update ContactInfo set disabled=0 where contactId" . sql_in_numeric_set($ids) . " and contactId!=" . $contact->contactId); if ($result && $result->affected_rows) { return self::modify_password_mail("password='' and contactId!=" . $contact->contactId, true, "create", $ids); } else { if ($result) { return (object) array("ok" => true, "warnings" => array("Those accounts were already enabled.")); } else { return (object) array("error" => true); } } }
function update_schema_drop_keys_if_exist($conf, $table, $key) { $indexes = Dbl::fetch_first_columns($conf->dblink, "select distinct index_name from information_schema.statistics where table_schema=database() and `table_name`='{$table}'"); $drops = []; foreach (is_array($key) ? $key : [$key] as $k) { if (in_array($k, $indexes)) { $drops[] = $k === "PRIMARY" ? "drop primary key" : "drop key `{$k}`"; } } if (count($drops)) { return $conf->ql("alter table `{$table}` " . join(", ", $drops)); } else { return true; } }
public function json() { // find out who is light and who is heavy // (light => less than 0.66 * (80th percentile)) $nass = array(); foreach ($this->r as $cid => $x) { $nass[] = count($x); } sort($nass); $heavy_boundary = 0; if (count($nass)) { $heavy_boundary = 0.66 * $nass[(int) (0.8 * count($nass))]; } $contacts = pcMembers(); $need_contacts = []; foreach ($this->r as $cid => $x) { if (!isset($contacts[$cid]) && ctype_digit($cid)) { $need_contacts[] = $cid; } } if (count($need_contacts)) { $result = Dbl::q("select firstName, lastName, affiliation, email, contactId, roles, contactTags, disabled from ContactInfo where contactId ?a", $need_contacts); while ($result && ($row = Contact::fetch($result))) { $contacts[$row->contactId] = $row; } } $users = array(); $tags = $this->contact->can_view_reviewer_tags(); foreach ($this->r as $cid => $x) { if ($cid != "conflicts") { $users[$cid] = $u = (object) array(); $p = get($contacts, $cid); if ($p) { $u->name = Text::name_text($p); } if (count($x) < $heavy_boundary) { $u->light = true; } if ($p && $tags && ($t = $p->viewable_color_classes($this->contact))) { $u->color_classes = $t; } } } return (object) array("reviews" => $this->r, "deadlines" => $this->dl, "users" => $users); }
public function order_anno_list() { if ($this->order_anno_list == false) { $this->order_anno_list = Dbl::fetch_objects("select * from PaperTagAnno where tag=?", $this->tag); $this->order_anno_list[] = (object) ["tag" => $this->tag, "tagIndex" => TAG_INDEXBOUND, "heading" => "Untagged", "annoId" => null, "annoFormat" => 0]; usort($this->order_anno_list, function ($a, $b) { if ($a->tagIndex != $b->tagIndex) { return $a->tagIndex < $b->tagIndex ? -1 : 1; } else { if (($x = strcasecmp($a->heading, $b->heading)) != 0) { return $x; } else { return $a->annoId < $b->annoId ? -1 : 1; } } }); } return $this->order_anno_list; }
function saveAssignments($qreq, $reviewer) { global $Conf, $Me, $Now, $pcm; $reviewer_contact = $pcm[$reviewer]; $round_number = null; if (!count($qreq->assrev)) { return; } $result = Dbl::qe_raw($Conf->paperQuery($Me, array("paperId" => array_keys($qreq->assrev), "reviewer" => $reviewer))); $lastPaperId = -1; $del = $ins = ""; while ($row = PaperInfo::fetch($result, $Me)) { if ($row->paperId == $lastPaperId || !$Me->can_administer($row) || $row->reviewerConflictType >= CONFLICT_AUTHOR || !isset($qreq->assrev[$row->paperId])) { continue; } $lastPaperId = $row->paperId; $type = $qreq->assrev[$row->paperId]; if ($type >= 0 && $row->reviewerConflictType > 0 && $row->reviewerConflictType < CONFLICT_AUTHOR) { $del .= " or paperId={$row->paperId}"; } if ($type < 0 && $row->reviewerConflictType < CONFLICT_CHAIRMARK) { $ins .= ", ({$row->paperId}, {$reviewer}, " . CONFLICT_CHAIRMARK . ")"; } if ($qreq->kind == "a" && $type != $row->reviewerReviewType && ($type <= 0 || $reviewer_contact->can_accept_review_assignment_ignore_conflict($row))) { if ($type > 0 && $round_number === null) { $round_number = $Conf->round_number($qreq->rev_roundtag, true); } $Me->assign_review($row->paperId, $reviewer, $type, array("round_number" => $round_number)); } } if ($ins) { $Conf->qe("insert into PaperConflict (paperId, contactId, conflictType) values " . substr($ins, 2) . " on duplicate key update conflictType=greatest(conflictType,values(conflictType))"); } if ($del) { $Conf->qe("delete from PaperConflict where contactId={$reviewer} and (" . substr($del, 4) . ")"); } $Conf->update_rev_tokens_setting(false); if ($Conf->setting("pcrev_assigntime") == $Now) { $Conf->confirmMsg("Assignments saved! You may want to <a href=\"" . hoturl("mail", "template=newpcrev") . "\">send mail about the new assignments</a>."); } redirectSelf(["kind" => $qreq->kind]); }
function run(Contact $user, $qreq, $ssel) { global $Conf, $Opt; $q = $Conf->paperQuery($user, ["paperId" => $ssel->selection(), "topics" => true, "options" => true]); $result = Dbl::qe_raw($q); $pj = []; $ps = new PaperStatus($user, ["forceShow" => true, "hide_docids" => true]); if ($this->iszip) { $this->zipdoc = new ZipDocument($Opt["downloadPrefix"] . "data.zip"); $ps->add_document_callback([$this, "document_callback"]); } while ($prow = PaperInfo::fetch($result, $user)) { if ($user->can_administer($prow, true)) { $pj[$prow->paperId] = $ps->paper_json($prow); } else { $pj[$prow->paperId] = (object) ["pid" => $prow->paperId, "error" => "You don’t have permission to administer this paper."]; if ($this->iszip) { $this->zipdoc->warnings[] = "#{$prow->paperId}: You don’t have permission to administer this paper."; } } } $pj = array_values($ssel->reorder($pj)); if (count($pj) == 1) { $pj = $pj[0]; $pj_filename = $Opt["downloadPrefix"] . "paper" . $ssel->selection_at(0) . "-data.json"; } else { $pj_filename = $Opt["downloadPrefix"] . "data.json"; } if ($this->iszip) { $this->zipdoc->add(json_encode($pj, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n", $pj_filename); $this->zipdoc->download(); } else { header("Content-Type: application/json"); header("Content-Disposition: attachment; filename=" . mime_quote_string($pj_filename)); echo json_encode($pj, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n"; } exit; }
function run(Contact $user, $qreq, $ssel) { global $Conf, $Opt; $q = $Conf->paperQuery($user, array("paperId" => $ssel->selection(), "allReviewerPreference" => 1, "allConflictType" => 1, "topics" => 1)); $result = Dbl::qe_raw($q); $texts = array(); $pcm = pcMembers(); $has_conflict = $has_expertise = $has_topic_score = false; while ($prow = PaperInfo::fetch($result, $user)) { if (!$user->can_administer($prow, true)) { continue; } $conflicts = $prow->conflicts(); foreach ($pcm as $cid => $p) { $pref = $prow->reviewer_preference($p); $conf = get($conflicts, $cid); $tv = $prow->topicIds ? $prow->topic_interest_score($p) : 0; if ($pref || $conf || $tv) { $texts[$prow->paperId][] = array("paper" => $prow->paperId, "title" => $prow->title, "first" => $p->firstName, "last" => $p->lastName, "email" => $p->email, "preference" => $pref[0] ?: "", "expertise" => unparse_expertise($pref[1]), "topic_score" => $tv ?: "", "conflict" => $conf ? "conflict" : ""); $has_conflict = $has_conflict || $conf; $has_expertise = $has_expertise || $pref[1] !== null; $has_topic_score = $has_topic_score || $tv; } } } $headers = array("paper", "title", "first", "last", "email", "preference"); if ($has_expertise) { $headers[] = "expertise"; } if ($has_topic_score) { $headers[] = "topic_score"; } if ($has_conflict) { $headers[] = "conflict"; } downloadCSV($ssel->reorder($texts), $headers, "allprefs", ["selection" => true]); }
function fix_one_delegation() { global $Conf; $row = Dbl::fetch_first_row("select r.paperId, r.contactId, u.email, q.ct, q.cs, r.reviewNeedsSubmit\n from PaperReview r\n left join (select paperId, requestedBy, count(reviewId) ct, count(reviewSubmitted) cs\n from PaperReview where reviewType<" . REVIEW_SECONDARY . "\n group by paperId, requestedBy) q\n on (q.paperId=r.paperId and q.requestedBy=r.contactId)\n left join ContactInfo u on (u.contactId=r.contactId)\n where r.reviewType=" . REVIEW_SECONDARY . " and r.reviewSubmitted is null\n and if(coalesce(q.ct,0)=0,1,if(q.cs=0,-1,0))!=r.reviewNeedsSubmit\n limit 1"); if (!$row) { return false; } $pid = (int) $row[0]; $req_cid = (int) $row[1]; $req_email = $row[2]; $prow = $Conf->paperRow(["paperId" => $pid], null); fwrite(STDERR, "Problem: #{$pid} review by {$req_email}\n"); fwrite(STDERR, " reviewNeedsSubmit {$row['5']}, " . plural($row[3] ?: 0, "delegate") . ", " . plural($row[4] ?: 0, "submitted delegate") . "\n"); $result = Dbl::qe("select l.* from ActionLog l where paperId=? order by logId asc", $pid); $proposals = $confirmations = []; while ($row = edb_orow($result)) { if ($row->contactId == $req_cid && preg_match('/\\ALogged proposal for (\\S+) to review/', $row->action, $m) && ($xid = Contact::id_by_email($m[1]))) { $proposals[$xid] = true; } else { if (preg_match('/\\AAdded External review by (\\S+)/', $row->action, $m) && ($pc = pcByEmail($m[1])) && $pc->can_administer($prow)) { $confirmations[$row->contactId] = $pc->contactId; } } } Dbl::free($result); foreach ($proposals as $xid => $x) { if (isset($confirmations[$xid])) { $result1 = Dbl::qe("update PaperReview set requestedBy=? where paperId=? and contactId=? and requestedBy=?", $req_cid, $pid, $xid, $confirmations[$xid]); $result2 = Dbl::qe("update PaperReview r, PaperReview q set r.reviewNeedsSubmit=0 where r.paperId=? and r.contactId=? and q.paperId=? and q.contactId=? and q.reviewSubmitted is not null", $pid, $req_cid, $pid, $xid); if ($result1->affected_rows || $result2->affected_rows) { return true; } } } error_log("Failed to resolve paper #{$pid} review by {$req_email}"); return false; }
static function enable($ids, $contact) { global $Conf; $old_logged_errors = Dbl::$logged_errors; Dbl::qe("update ContactInfo set disabled=1 where contactId ?a and password='' and contactId!=?", $ids, $contact->contactId); $disabled_cids = Dbl::fetch_first_columns("select contactId from ContactInfo where contactId ?a and disabled=1 and contactId!=?", $ids, $contact->contactId); if ($disabled_cids) { Dbl::qe("update ContactInfo set disabled=0 where contactId ?a", $disabled_cids); } if (Dbl::$logged_errors > $old_logged_errors) { return (object) ["error" => true]; } else { if (!count($disabled_cids)) { return (object) ["ok" => true, "warnings" => ["Those accounts were already enabled."]]; } else { $Conf->save_logs(true); foreach ($disabled_cids as $cid) { $Conf->log("Account enabled by {$contact->email}", $cid); } $Conf->save_logs(false); return self::modify_password_mail("password='' and contactId!=" . $contact->contactId, true, "create", $disabled_cids); } } }
function check_repo_open() { global $Now; // Recheck repository openness after a day for closed repositories, // and after 30 seconds for open or failed-check repositories. if (!$this->repo || !$this->repo->open && $Now - $this->repo->opencheckat <= 86400) { return 0; } else { if ($this->repo->open && $Now - $this->repo->opencheckat <= 30) { return (int) $this->repo->open; } } $r = $this->is_repo_open(); if ($r != $this->repo->open || $Now != $this->repo->opencheckat) { Dbl::qe("update Repository set `open`=?, opencheckat=? where repoid=?", $r, $Now, $this->repo->repoid); $this->repo->open = $r; $this->repo->opencheckat = $Now; } return $r; }
} if (@$_POST["update"] && check_post()) { $ck = $cv = array(); $roles = 0; if (@$_POST["pctype"] === "chair") { $roles |= Contact::ROLE_CHAIR | Contact::ROLE_PC; } else { if (@$_POST["pctype"] === "pc") { $roles |= Contact::ROLE_PC; } } if (@$_POST["sysadmin"]) { $roles |= Contact::ROLE_ADMIN; } $ck[] = "roles={$roles}"; Dbl::qe_apply("update ContactInfo set " . join($ck, ",") . " where contactId=" . $User->contactId, $cv); redirectSelf(); } $Conf->header("Profile", "profile"); $xsep = " <span class='barsep'> | </span> "; echo "<div id='homeinfo'>"; echo "<h2 class='homeemail'>", Text::user_html($User), "</h2>"; if ($User->seascode_username || $User->huid) { echo '<h3><a href="', hoturl("index", array("u" => $Me->user_linkpart($User))), '">', htmlspecialchars($User->seascode_username ?: $User->huid), '</a>'; if ($Me->privChair) { echo " ", become_user_link($User); } echo "</h3>"; } if ($User->dropped) { ContactView::echo_group("", '<strong class="err">You have dropped the course.</strong> If this is incorrect, contact us.');
if (!$zlib_output_compression) { header("Content-Length: 43"); } print "GIF89a€!ù,D;"; } exit; } if (!$Me->isPC) { $Me->escape(); } function output($User) { global $Me; $u = $Me->user_linkpart($User); echo '<div class="facebook61">', '<a href="', hoturl("index", ["u" => $u]), '">', '<img class="bigface61" src="' . hoturl("face", ["u" => $u, "imageid" => $User->contactImageId ?: 0]) . '" border="0" />', '</a>', '<h2 class="infacebook61"><a class="q" href="', hoturl("index", ["u" => $u]), '">', htmlspecialchars($u), '</a>'; if ($Me->privChair) { echo " ", become_user_link($User); } echo '</h2>'; if ($User !== $Me) { echo '<h3 class="infacebook61">', Text::user_html($User), '</h3>'; } echo '</div>'; } $Conf->header("Thefacebook", "face"); $u = Dbl::qe("select contactId, email, firstName, lastName, seascode_username, contactImageId from ContactInfo where roles=0"); while ($user = edb_orow($u)) { output($user); } echo "<div class='clear'></div>\n"; $Conf->footer();
private function _prepare_sort() { global $Conf; $this->default_sort_column = PaperColumn::lookup("id"); $this->sorters[0]->field = null; if ($this->search->sorters) { foreach ($this->search->sorters as $sorter) { if ($sorter->type && ($field = PaperColumn::lookup($sorter->type)) && $field->prepare($this, PaperColumn::PREP_SORT) && $field->comparator) { $sorter->field = $field; } else { if ($sorter->type) { if ($this->contact->can_view_tags(null) && ($tagger = new Tagger()) && ($tag = $tagger->check($sorter->type)) && ($result = Dbl::qe("select paperId from PaperTag where tag=? limit 1", $tag)) && edb_nrows($result)) { $this->search->warn("Unrecognized sort “" . htmlspecialchars($sorter->type) . "”. Did you mean “sort:#" . htmlspecialchars($sorter->type) . "”?"); } else { $this->search->warn("Unrecognized sort “" . htmlspecialchars($sorter->type) . "”."); } continue; } } ListSorter::push($this->sorters, $sorter); } if (count($this->sorters) > 1 && $this->sorters[0]->empty) { array_shift($this->sorters); } } if (get($this->sorters[0], "field")) { /* all set */ } else { if ($this->sorters[0]->type && ($c = PaperColumn::lookup($this->sorters[0]->type)) && $c->prepare($this, PaperColumn::PREP_SORT)) { $this->sorters[0]->field = $c; } else { $this->sorters[0]->field = $this->default_sort_column; } } $this->sorters[0]->type = $this->sorters[0]->field->name; // set defaults foreach ($this->sorters as $s) { if ($s->reverse === null) { $s->reverse = false; } if ($s->score === null) { $s->score = ListSorter::default_score_sort(); } } }
function createAnonymousReview() { global $Conf, $Me, $Now, $prow, $rrows; Dbl::qe_raw("lock tables PaperReview write, PaperReviewRefused write, ContactInfo write, PaperConflict read, ActionLog write"); // find an unassigned anonymous review contact $contactemail = unassignedAnonymousContact(); $result = Dbl::qe_raw("select contactId from ContactInfo where email='" . sqlq($contactemail) . "'"); if (edb_nrows($result) == 1) { $row = edb_row($result); $reqId = $row[0]; } else { $result = Dbl::qe("insert into ContactInfo set firstName='Jane Q.', lastName='Public', unaccentedName='Jane Q. Public', email=?, affiliation='Unaffiliated', password='', disabled=1, creationTime={$Now}", $contactemail); if (!$result) { return $result; } $reqId = $result->insert_id; } // store the review request $reviewId = $Me->assign_review($prow->paperId, $reqId, REVIEW_EXTERNAL, array("mark_notify" => true, "token" => true)); if ($reviewId) { $result = Dbl::ql("select reviewToken from PaperReview where reviewId={$reviewId}"); $row = edb_row($result); $Conf->confirmMsg("Created a new anonymous review for paper #{$prow->paperId}. The review token is " . encode_token((int) $row[0]) . "."); } Dbl::qx_raw("unlock tables"); $Conf->update_rev_tokens_setting(true); return true; }
private static function try_list($opt, $listtype, $sort = null) { global $Conf, $Me; if ($listtype == "u" && $Me->privChair) { $searchtype = defval($opt, "t") === "all" ? "all" : "pc"; $q = "select contactId from ContactInfo"; if ($searchtype == "pc") { $q .= " where (roles&" . Contact::ROLE_PC . ")!=0"; } $result = Dbl::ql("{$q} order by lastName, firstName, email"); $a = array(); while ($row = edb_row($result)) { $a[] = (int) $row[0]; } Dbl::free($result); return self::create("u/" . $searchtype, $a, $searchtype == "pc" ? "Program committee" : "Users", hoturl_site_relative_raw("users", "t={$searchtype}")); } else { $search = new PaperSearch($Me, $opt); $x = $search->session_list_object($sort); if ($sort || $search->has_sort()) { $pl = new PaperList($search, array("sort" => $sort)); $x->ids = $pl->id_array(); } return $x; } }
private static function load_optdata($pid) { $result = Dbl::qe("select optionId, value, data from PaperOption where paperId=?", $pid); $optdata = array(); while ($row = edb_row($result)) { $optdata[$row[0] . "." . $row[1]] = $row[2]; } Dbl::free($result); return $optdata; }
function show_pset_table($pset) { global $Conf, $Me, $Now, $Profile, $LastPsetFix; echo '<div id="', $pset->urlkey, '">'; echo "<h3>", htmlspecialchars($pset->title), "</h3>"; if ($Me->privChair) { show_pset_actions($pset); } if ($pset->disabled) { echo "</div>\n"; return; } $t0 = $Profile ? microtime(true) : 0; // load students if ($Conf->opt("restrictRepoView")) { $view = "l2.link repoviewable"; $viewjoin = "left join ContactLink l2 on (l2.cid=c.contactId and l2.type=" . LINK_REPOVIEW . " and l2.link=l.link)\n"; } else { $view = "4 repoviewable"; $viewjoin = ""; } $result = Dbl::qe("select c.contactId, c.firstName, c.lastName, c.email,\n\tc.huid, c.github_username, c.seascode_username, c.anon_username, c.extension, c.disabled, c.dropped, c.roles, c.contactTags,\n\tgroup_concat(pl.link) pcid, group_concat(rpl.link) rpcid,\n\tr.repoid, r.cacheid, r.heads, r.url, r.open, r.working, r.lastpset, r.snapcheckat, {$view},\n\trg.gradehash, rg.gradercid, rg.placeholder, rg.placeholder_at\n\tfrom ContactInfo c\n\tleft join ContactLink l on (l.cid=c.contactId and l.type=" . LINK_REPO . " and l.pset={$pset->id})\n\t{$viewjoin}\n\tleft join Repository r on (r.repoid=l.link)\n\tleft join ContactLink pl on (pl.cid=c.contactId and pl.type=" . LINK_PARTNER . " and pl.pset={$pset->id})\n\tleft join ContactLink rpl on (rpl.cid=c.contactId and rpl.type=" . LINK_BACKPARTNER . " and rpl.pset={$pset->id})\n\tleft join RepositoryGrade rg on (rg.repoid=r.repoid and rg.pset={$pset->id})\n\twhere (c.roles&" . Contact::ROLE_PCLIKE . ")=0\n\tand (rg.repoid is not null or not c.dropped)\n\tgroup by c.contactId, r.repoid"); $t1 = $Profile ? microtime(true) : 0; $anonymous = $pset->anonymous; if (req("anonymous") !== null && $Me->privChair) { $anonymous = !!req("anonymous"); } $students = array(); while ($result && ($s = Contact::fetch($result))) { $s->set_anonymous($anonymous); Contact::set_sorter($s, req("sort")); $students[$s->contactId] = $s; // maybe lastpset links are out of order if ($s->lastpset < $pset) { $LastPsetFix = true; } } uasort($students, "Contact::compare"); $checkbox = $Me->privChair || !$pset->gitless && $pset->runners; $rows = array(); $max_ncol = 0; $incomplete = array(); $pcmembers = pcMembers(); $jx = []; foreach ($students as $s) { if (!$s->visited) { $row = (object) ["student" => $s, "text" => "", "ptext" => []]; $j = render_pset_row($pset, $students, $s, $row, $pcmembers, $anonymous); if ($s->pcid) { foreach (array_unique(explode(",", $s->pcid)) as $pcid) { if (isset($students[$pcid])) { $jj = render_pset_row($pset, $students, $students[$pcid], $row, $pcmembers, $anonymous); $j["partners"][] = $jj; } } } if ($row->sortprefix) { $j["boring"] = true; } $jx[$row->sortprefix . $s->sorter] = $j; $max_ncol = max($max_ncol, $row->ncol); if ($s->incomplete) { $u = $Me->user_linkpart($s); $incomplete[] = '<a href="' . hoturl("pset", array("pset" => $pset->urlkey, "u" => $u, "sort" => req("sort"))) . '">' . htmlspecialchars($u) . '</a>'; } } } if (count($incomplete)) { echo '<div id="incomplete_pset', $pset->id, '" style="display:none" class="merror">', '<strong>', htmlspecialchars($pset->title), '</strong>: ', 'Your grading is incomplete. Missing grades: ', join(", ", $incomplete), '</div>', '<script>jQuery("#incomplete_pset', $pset->id, '").remove().show().appendTo("#incomplete_notices")</script>'; } if ($checkbox) { echo Ht::form_div(hoturl_post("index", array("pset" => $pset->urlkey, "save" => 1))); } $sort_key = $anonymous ? "anon_username" : "username"; usort($jx, function ($a, $b) use($sort_key) { if (get($a, "boring") != get($b, "boring")) { return get($a, "boring") ? 1 : -1; } return strcmp($a[$sort_key], $b[$sort_key]); }); echo '<table class="s61', $anonymous ? " s61anonymous" : "", '" id="pa-pset' . $pset->id . '"></table>'; $jd = ["checkbox" => $checkbox, "anonymous" => $anonymous, "grade_keys" => array_keys($pset->grades), "gitless" => $pset->gitless, "gitless_grades" => $pset->gitless_grades, "urlpattern" => hoturl("pset", ["pset" => $pset->urlkey, "u" => "@", "sort" => req("sort")])]; $i = $nintotal = $last_in_total = 0; foreach ($pset->grades as $ge) { if (!$ge->no_total) { ++$nintotal; $last_in_total = $ge->name; } ++$i; } if ($nintotal > 1) { $jd["need_total"] = true; } else { if ($nintotal == 1) { $jd["total_key"] = $last_in_total; } } echo Ht::unstash(), '<script>pa_render_pset_table(', $pset->id, ',', json_encode($jd), ',', json_encode(array_values($jx)), ')</script>'; if ($Me->privChair && !$pset->gitless_grades) { echo "<div class='g'></div>"; $sel = array("none" => "N/A"); foreach (pcMembers() as $pcm) { $sel[$pcm->email] = Text::name_html($pcm); } $sel["__random__"] = "Random"; echo '<span class="nb" style="padding-right:2em">', Ht::select("grader", $sel, "none"), Ht::submit("setgrader", "Set grader"), '</span>'; } if (!$pset->gitless) { $sel = array(); foreach ($pset->runners as $r) { if ($Me->can_run($pset, $r)) { $sel[$r->name] = htmlspecialchars($r->title); } } if (count($sel)) { echo '<span class="nb" style="padding-right:2em">', Ht::select("runner", $sel), Ht::submit("runmany", "Run all"), '</span>'; } } if ($checkbox) { echo "</div></form>\n"; } if ($Profile) { $t2 = microtime(true); echo sprintf("<div>Δt %.06f DB, %.06f total</div>", $t1 - $t0, $t2 - $t0); } echo "</div>\n"; }
xassert(!$Conf->check_tracks($paper17, $user_jon, Track::ASSREV)); xassert(!$user_jon->can_accept_review_assignment_ignore_conflict($paper17)); xassert(!$user_jon->can_accept_review_assignment($paper17)); // check shepherd search visibility $paper11 = $Conf->paperRow(11, $user_chair); $paper12 = $Conf->paperRow(12, $user_chair); $j = call_api("setshepherd", $user_chair, ["shepherd" => $user_estrin->email], $paper11); xassert_eqq($j->ok, true); $j = call_api("setshepherd", $user_chair, ["shepherd" => $user_estrin->email], $paper12); xassert_eqq($j->ok, true); assert_search_papers($user_chair, "shep:any", "11 12"); assert_search_papers($user_chair, "shep:estrin", "11 12"); assert_search_papers($user_shenker, "shep:any", "11 12"); // tag searches assert_search_papers($user_chair, "#green", "3 9 13 17"); Dbl::qe("insert into PaperTag (paperId,tag,tagIndex) values (1,?,10), (1,?,5), (2,?,3)", $user_jon->cid . "~vote", $user_marina->cid . "~vote", $user_marina->cid . "~vote"); assert_search_papers($user_jon, "#~vote", "1"); assert_search_papers($user_jon, "#~vote≥10", "1"); assert_search_papers($user_jon, "#~vote>10", ""); assert_search_papers($user_jon, "#~vote=10", "1"); assert_search_papers($user_jon, "#~vote<10", ""); assert_search_papers($user_marina, "#~vote", "1 2"); assert_search_papers($user_marina, "#~vote≥5", "1"); assert_search_papers($user_marina, "#~vote>5", ""); assert_search_papers($user_marina, "#~vote=5", "1"); assert_search_papers($user_marina, "#~vote<5", "2"); assert_search_papers($user_chair, "#marina~vote", "1 2"); assert_search_papers($user_chair, "#red~vote", "1"); // assign some tags using AssignmentSet interface $assignset = new AssignmentSet($Admin, true); $assignset->parse("paper,action,tag,index\n1-9,tag,g*#clear\n2,tag,green,1\n");
private function run() { global $Conf, $Opt, $Me, $Error, $subjectPrefix, $mailer_options; $subject = trim(defval($_REQUEST, "subject", "")); if (substr($subject, 0, strlen($subjectPrefix)) != $subjectPrefix) { $subject = $subjectPrefix . $subject; } $emailBody = $_REQUEST["emailBody"]; $template = array("subject" => $subject, "body" => $emailBody); $rest = array("cc" => $_REQUEST["cc"], "reply-to" => $_REQUEST["replyto"], "no_error_quit" => true); $rest = array_merge($rest, $mailer_options); // test whether this mail is paper-sensitive $mailer = new HotCRPMailer($Me, null, $rest); $prep = $mailer->make_preparation($template, $rest); $paper_sensitive = preg_match('/%[A-Z0-9]+[(%]/', $prep->subject . $prep->body); $q = $this->recip->query($paper_sensitive); if (!$q) { return Conf::msg_error("Bad recipients value"); } $result = $Conf->qe($q); if (!$result) { return; } $recipients = defval($_REQUEST, "recipients", ""); if ($this->sending) { $q = "recipients='" . sqlq($recipients) . "', cc='" . sqlq($_REQUEST["cc"]) . "', replyto='" . sqlq($_REQUEST["replyto"]) . "', subject='" . sqlq($_REQUEST["subject"]) . "', emailBody='" . sqlq($_REQUEST["emailBody"]) . "'"; if ($Conf->sversion >= 79) { $q .= ", q='" . sqlq($_REQUEST["q"]) . "', t='" . sqlq($_REQUEST["t"]) . "'"; } if ($log_result = Dbl::query_raw("insert into MailLog set {$q}")) { $this->mailid_text = " #" . $log_result->insert_id; } $Me->log_activity("Sending mail{$this->mailid_text} \"{$subject}\""); } else { $rest["no_send"] = true; } $mailer = new HotCRPMailer(); $mailer->combination_type = $this->recip->combination_type($paper_sensitive); $fake_prep = new HotCRPMailPreparation(); $fake_prep->fake = true; $last_prep = $fake_prep; $nrows_done = 0; $nrows_left = edb_nrows($result); $nwarnings = 0; $preperrors = array(); $revinform = $recipients == "newpcrev" ? array() : null; while ($row = PaperInfo::fetch($result, $Me)) { ++$nrows_done; $contact = new Contact($row); $rest["newrev_since"] = $this->recip->newrev_since; $mailer->reset($contact, $row, $rest); $prep = $mailer->make_preparation($template, $rest); if ($prep->errors) { foreach ($prep->errors as $lcfield => $hline) { $reqfield = $lcfield == "reply-to" ? "replyto" : $lcfield; $Error[$reqfield] = true; $emsg = Mailer::$email_fields[$lcfield] . " destination isn’t a valid email list: <blockquote><tt>" . htmlspecialchars($hline) . "</tt></blockquote> Make sure email address are separated by commas; put names in \"quotes\" and email addresses in <angle brackets>."; if (!isset($preperrors[$emsg])) { Conf::msg_error($emsg); } $preperrors[$emsg] = true; } } else { if ($this->process_prep($prep, $last_prep, $row)) { if ((!$Me->privChair || @$Opt["chairHidePasswords"]) && !@$last_prep->sensitive) { $srest = array_merge($rest, array("sensitivity" => "display")); $mailer->reset($contact, $row, $srest); $last_prep->sensitive = $mailer->make_preparation($template, $srest); } } } if ($nwarnings != $mailer->nwarnings() || $nrows_done % 5 == 0) { $this->echo_mailinfo($nrows_done, $nrows_left); } if ($nwarnings != $mailer->nwarnings()) { $this->echo_prologue(); $nwarnings = $mailer->nwarnings(); echo "<div id='foldmailwarn{$nwarnings}' class='hidden'><div class='warning'>", join("<br />", $mailer->warnings()), "</div></div>"; $Conf->echoScript("\$\$('mailwarnings').innerHTML = \$\$('foldmailwarn{$nwarnings}').innerHTML;"); } if ($this->sending && $revinform !== null) { $revinform[] = "(paperId={$row->paperId} and contactId={$row->contactId})"; } } $this->process_prep($fake_prep, $last_prep, (object) array("paperId" => -1)); $this->echo_mailinfo($nrows_done, $nrows_left); if (!$this->started && !count($preperrors)) { return Conf::msg_error("No users match “" . $this->recip->unparse() . "” for that search."); } else { if (!$this->started) { return false; } else { if (!$this->sending) { $this->echo_actions(); } } } if ($revinform) { $Conf->qe("update PaperReview set timeRequestNotified=" . time() . " where " . join(" or ", $revinform)); } echo "</div></form>"; $Conf->echoScript("fold('mail', null);"); $Conf->footer(); exit; }
static function pcassignments_csv_data($user, $selection) { global $Conf; $pcm = pcMembers(); $round_list = $Conf->round_list(); $reviewnames = array(REVIEW_PC => "pcreview", REVIEW_SECONDARY => "secondary", REVIEW_PRIMARY => "primary"); $any_round = false; $texts = array(); $result = Dbl::qe_raw($Conf->paperQuery($user, array("paperId" => $selection, "assignments" => 1))); while ($prow = PaperInfo::fetch($result, $user)) { if (!$user->allow_administer($prow)) { $texts[] = array(); $texts[] = array("paper" => $prow->paperId, "action" => "none", "title" => "You cannot override your conflict with this paper"); } else { if ($prow->all_reviewers()) { $texts[] = array(); $texts[] = array("paper" => $prow->paperId, "action" => "clearreview", "email" => "#pc", "round" => "any", "title" => $prow->title); foreach ($prow->all_reviewers() as $cid) { if (($pc = get($pcm, $cid)) && ($rtype = $prow->review_type($cid)) >= REVIEW_PC) { $round = $prow->review_round($cid); $round_name = $round ? $round_list[$round] : "none"; $any_round = $any_round || $round != 0; $texts[] = array("paper" => $prow->paperId, "action" => $reviewnames[$rtype], "email" => $pc->email, "round" => $round_name); } } } } } $header = array("paper", "action", "email"); if ($any_round) { $header[] = "round"; } $header[] = "title"; return [$header, $texts]; }
static function fail_bad_database() { global $Conf, $Opt; $errors = array(); if (get($Opt, "multiconference") && $Opt["confid"] === "__nonexistent__") { $errors[] = "You haven’t specified a conference and this is a multiconference installation."; } else { if (get($Opt, "multiconference")) { $errors[] = "The “" . $Opt["confid"] . "” conference does not exist. Check your URL to make sure you spelled it correctly."; } else { $errors[] = "HotCRP was unable to load. A system administrator must fix this problem."; $errors[] = "Error: Unable to connect to database " . Dbl::sanitize_dsn($Conf->dsn); if (defined("HOTCRP_TESTHARNESS")) { $errors[] = "You may need to run `lib/createdb.sh -c test/options.php` to create the database."; } } } self::fail_message($errors); }
static function check_repo($repo, $delta, $foreground = false) { global $ConfSitePATH, $Now; assert(isset($repo->repoid) && isset($repo->cacheid) && isset($repo->url) && property_exists($repo, "snapcheckat")); if ($repo->repoid && (!$repo->snapcheckat || $repo->snapcheckat + $delta <= $Now)) { Dbl::qe("update Repository set snapcheckat={$Now} where repoid={$repo->repoid}"); $repo->snapcheckat = $Now; if ($foreground) { set_time_limit(30); } // see also handout_repo $command = "{$ConfSitePATH}/src/gitfetch {$repo->repoid} {$repo->cacheid} " . escapeshellarg($repo->ssh_url()) . " 1>&2" . ($foreground ? "" : " &"); shell_exec($command); } }
function run(Contact $user, $qreq, $ssel) { global $Conf; $mt = $qreq->assignfn; $mpc = (string) $qreq->markpc; $pc = null; if ($mpc != "" && $mpc != "0") { $pc = Contact::find_by_email($mpc); } if ($mt == "auto") { $t = in_array($qreq->t, array("acc", "s")) ? $qreq->t : "all"; $q = join("+", $ssel->selection()); go(hoturl("autoassign", "pap={$q}&t={$t}&q={$q}")); } else { if ($mt == "lead" || $mt == "shepherd") { if ($user->assign_paper_pc($ssel->selection(), $mt, $pc)) { $Conf->confirmMsg(ucfirst(pluralx($ssel->selection(), $mt)) . " set."); } else { if ($OK) { $Conf->confirmMsg("No changes."); } } } else { if (!$pc) { Conf::msg_error("“" . htmlspecialchars($mpc) . "” is not a PC member."); } else { if ($mt == "conflict" || $mt == "unconflict") { if ($mt == "conflict") { Dbl::qe("insert into PaperConflict (paperId, contactId, conflictType) (select paperId, ?, ? from Paper where paperId" . $ssel->sql_predicate() . ") on duplicate key update conflictType=greatest(conflictType, values(conflictType))", $pc->contactId, CONFLICT_CHAIRMARK); $user->log_activity("Mark conflicts with {$mpc}", $ssel->selection()); } else { Dbl::qe("delete from PaperConflict where PaperConflict.conflictType<? and contactId=? and (paperId" . $ssel->sql_predicate() . ")", CONFLICT_AUTHOR, $pc->contactId); $user->log_activity("Remove conflicts with {$mpc}", $ssel->selection()); } } else { if (substr($mt, 0, 6) == "assign" && ($asstype = substr($mt, 6)) && isset(ReviewForm::$revtype_names[$asstype])) { Dbl::qe_raw("lock tables PaperConflict write, PaperReview write, PaperReviewRefused write, Paper write, ActionLog write, Settings write"); $result = Dbl::qe_raw("select Paper.paperId, reviewId, reviewType, reviewModified, conflictType from Paper left join PaperReview on (Paper.paperId=PaperReview.paperId and PaperReview.contactId=" . $pc->contactId . ") left join PaperConflict on (Paper.paperId=PaperConflict.paperId and PaperConflict.contactId=" . $pc->contactId . ") where Paper.paperId" . $ssel->sql_predicate()); $conflicts = array(); $assigned = array(); $nworked = 0; while ($row = PaperInfo::fetch($result, $user)) { if ($asstype && $row->conflictType > 0) { $conflicts[] = $row->paperId; } else { if ($asstype && $row->reviewType >= REVIEW_PC && $asstype != $row->reviewType) { $assigned[] = $row->paperId; } else { $user->assign_review($row->paperId, $pc->contactId, $asstype); $nworked++; } } } if (count($conflicts)) { Conf::msg_error("Some papers were not assigned because of conflicts (" . join(", ", $conflicts) . "). If these conflicts are in error, remove them and try to assign again."); } if (count($assigned)) { Conf::msg_error("Some papers were not assigned because the PC member already had an assignment (" . join(", ", $assigned) . ")."); } if ($nworked) { $Conf->confirmMsg($asstype == 0 ? "Unassigned reviews." : "Assigned reviews."); } Dbl::qe_raw("unlock tables"); $Conf->update_rev_tokens_setting(false); } } } } } }
function load_pset_info() { global $ConfSitePATH, $Conf, $PsetInfo, $PsetOverrides, $Opt; // read initial messages Messages::$main = new Messages(); $x = json_decode(file_get_contents("{$ConfSitePATH}/src/messages.json")); foreach ($x as $j) { Messages::$main->add($j); } // read psets $PsetInfo = load_psets_json(false); // parse psets foreach ($PsetInfo as $pk => $p) { if (!is_object($p) || !isset($p->psetid)) { continue; } object_merge_recursive($p, $PsetInfo->_defaults); try { $pset = new Pset($pk, $p); Pset::register($pset); } catch (Exception $exception) { // Want to give a good error message, so discover where the error is. // - create pset landmark object $locinfo = (object) array(); foreach (psets_json_data(false) as $fname => $data) { $x = Json::decode_landmarks($data, $fname); object_replace_recursive($locinfo, $x); } $locp = $locinfo->{$pk}; if (isset($locinfo->_defaults)) { object_merge_recursive($locp, $locinfo->_defaults); } // - lookup exception path in landmark object $path = $exception instanceof PsetConfigException ? $exception->path : array(); for ($pathpos = 0; $pathpos < count($path) && $locp && !is_string($locp); ++$pathpos) { $component = $path[$pathpos]; $locp = is_array($locp) ? $locp[$component] : $locp->{$component}; } // - report error if (is_object($locp) && @$locp->__LANDMARK__) { $locp = $locp->__LANDMARK__; } else { if (!is_string($locp)) { $locp = $locinfo->{$pk}->__LANDMARK__; } } Multiconference::fail_message($locp . ": Configuration error: " . $exception->getMessage()); } } // read message data if (!@$PsetInfo->_messagedefs) { $PsetInfo->_messagedefs = (object) array(); } if (!@$PsetInfo->_messagedefs->SYSTEAM) { $PsetInfo->_messagedefs->SYSTEAM = "cs61-staff"; } foreach ($PsetInfo->_messagedefs as $k => $v) { Messages::$main->define($k, $v); } // also create log/ and repo/ directories foreach (array("{$ConfSitePATH}/log", "{$ConfSitePATH}/repo") as $d) { if (!is_dir($d) && !mkdir($d, 02770, true)) { $e = error_get_last(); Multiconference::fail_message("`{$d}` missing and cannot be created (" . $e["message"] . ")."); } if (!file_exists("{$d}/.htaccess") && ($x = file_get_contents("{$ConfSitePATH}/src/.htaccess")) !== false && file_put_contents("{$d}/.htaccess", $x) != strlen($x)) { Multiconference::fail_message("Error creating `{$d}/.htaccess`"); } } // if any anonymous problem sets, create anonymous usernames foreach (Pset::$all as $p) { if (!$p->disabled && $p->anonymous) { while ($row = Dbl::fetch_first_row(Dbl::qe("select contactId from ContactInfo where anon_username is null limit 1"))) { Dbl::q("update ContactInfo set anon_username='******' where contactId=?", $row[0]); } } } }
function assign_paper_pc($pids, $type, $reviewer, $extra = array()) { global $Conf; // check arguments assert($type == "lead" || $type == "shepherd" || $type == "manager"); if ($reviewer) { $revcid = is_object($reviewer) ? $reviewer->contactId : $reviewer; } else { $revcid = 0; } assert(is_int($revcid)); if (!is_array($pids)) { $pids = array($pids); } $px = array(); foreach ($pids as $p) { assert(is_object($p) && is_numeric($p->paperId) || is_numeric($p)); $px[] = (int) (is_object($p) ? $p->paperId : $p); } // make assignments if (isset($extra["old_cid"])) { $result = Dbl::qe("update Paper set {$type}ContactId=? where paperId" . sql_in_numeric_set($px) . " and {$type}ContactId=?", $revcid, $extra["old_cid"]); } else { $result = Dbl::qe("update Paper set {$type}ContactId=? where paperId" . sql_in_numeric_set($px), $revcid); } // log, update settings if ($result && $result->affected_rows) { $this->log_activity_for($revcid, "Set {$type}", $px); if (($type == "lead" || $type == "shepherd") && !$revcid != !$Conf->setting("paperlead")) { $Conf->update_paperlead_setting(); } if ($type == "manager" && !$revcid != !$Conf->setting("papermanager")) { $Conf->update_papermanager_setting(); } return true; } else { return false; } }
public function save($sv, $si) { global $Conf; if ($si->name == "tag_vote" && $sv->has_savedv("tag_vote")) { // check allotments $pcm = pcMembers(); foreach (preg_split('/\\s+/', $sv->savedv("tag_vote")) as $t) { if ($t === "") { continue; } $base = substr($t, 0, strpos($t, "#")); $allotment = substr($t, strlen($base) + 1); $result = Dbl::q("select paperId, tag, tagIndex from PaperTag where tag like '%~" . sqlq_for_like($base) . "'"); $pvals = array(); $cvals = array(); $negative = false; while ($row = edb_row($result)) { $who = substr($row[1], 0, strpos($row[1], "~")); if ($row[2] < 0) { $sv->set_error(null, "Removed " . Text::user_html($pcm[$who]) . "’s negative “{$base}” vote for paper #{$row['0']}."); $negative = true; } else { $pvals[$row[0]] = defval($pvals, $row[0], 0) + $row[2]; $cvals[$who] = defval($cvals, $who, 0) + $row[2]; } } foreach ($cvals as $who => $what) { if ($what > $allotment) { $sv->set_error("tag_vote", Text::user_html($pcm[$who]) . " already has more than {$allotment} votes for tag “{$base}”."); } } $q = $negative ? " or (tag like '%~" . sqlq_for_like($base) . "' and tagIndex<0)" : ""; $Conf->qe("delete from PaperTag where tag='" . sqlq($base) . "'{$q}"); $q = array(); foreach ($pvals as $pid => $what) { $q[] = "({$pid}, '" . sqlq($base) . "', {$what})"; } if (count($q) > 0) { $Conf->qe("insert into PaperTag values " . join(", ", $q)); } } } if ($si->name == "tag_approval" && $sv->has_savedv("tag_approval")) { $pcm = pcMembers(); foreach (preg_split('/\\s+/', $sv->savedv("tag_approval")) as $t) { if ($t === "") { continue; } $result = $Conf->q("select paperId, tag, tagIndex from PaperTag where tag like '%~" . sqlq_for_like($t) . "'"); $pvals = array(); $negative = false; while ($row = edb_row($result)) { $who = substr($row[1], 0, strpos($row[1], "~")); if ($row[2] < 0) { $sv->set_error(null, "Removed " . Text::user_html($pcm[$who]) . "’s negative “{$t}” approval vote for paper #{$row['0']}."); $negative = true; } else { $pvals[$row[0]] = defval($pvals, $row[0], 0) + 1; } } $q = $negative ? " or (tag like '%~" . sqlq_for_like($t) . "' and tagIndex<0)" : ""; $Conf->qe("delete from PaperTag where tag='" . sqlq($t) . "'{$q}"); $q = array(); foreach ($pvals as $pid => $what) { $q[] = "({$pid}, '" . sqlq($t) . "', {$what})"; } if (count($q) > 0) { $Conf->qe("insert into PaperTag values " . join(", ", $q)); } } } TagInfo::invalidate_defined_tags(); }