Пример #1
0
 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));
     }
 }
 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;
 }
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 save_paper_json($pj)
 {
     global $Conf, $Now;
     assert(!$this->hide_docids);
     $paperid = null;
     if (isset($pj->pid) && is_int($pj->pid) && $pj->pid > 0) {
         $paperid = $pj->pid;
     } else {
         if (!isset($pj->pid) && isset($pj->id) && is_int($pj->id) && $pj->id > 0) {
             $paperid = $pj->id;
         } else {
             if (isset($pj->pid) || isset($pj->id)) {
                 $key = isset($pj->pid) ? "pid" : "id";
                 $this->set_error_html($key, "Format error [{$key}]");
                 return false;
             }
         }
     }
     if (get($pj, "error") || get($pj, "error_html")) {
         $this->set_error_html("error", "Refusing to save paper with error");
         return false;
     }
     $this->prow = $old_pj = null;
     $this->paperid = $paperid ?: -1;
     if ($paperid) {
         $this->prow = $Conf->paperRow(["paperId" => $paperid, "topics" => true, "options" => true], $this->contact);
     }
     if ($this->prow) {
         $old_pj = $this->paper_json($this->prow, ["forceShow" => true]);
     }
     if ($pj && $old_pj && $paperid != $old_pj->pid) {
         $this->set_error_html("pid", "Saving paper with different ID");
         return false;
     }
     $this->normalize($pj, $old_pj);
     if ($old_pj) {
         $this->normalize($old_pj, null);
     }
     if ($this->nerrors) {
         return false;
     }
     $this->check_invariants($pj, $old_pj);
     // store documents (options already stored)
     if (isset($pj->submission) && $pj->submission) {
         $this->upload_document($pj->submission, PaperOption::find_document(DTYPE_SUBMISSION));
     }
     if (isset($pj->final) && $pj->final) {
         $this->upload_document($pj->final, PaperOption::find_document(DTYPE_FINAL));
     }
     // create contacts
     foreach (self::contacts_array($pj) as $c) {
         $c->only_if_contactdb = !get($c, "contact");
         $c->disabled = !!$this->disable_users;
         if (!Contact::create($c, !$this->no_email) && get($c, "contact")) {
             $this->set_error_html("contacts", "Could not create an account for contact " . Text::user_html($c) . ".");
         }
     }
     // catch errors
     if ($this->nerrors) {
         return false;
     }
     // update Paper table
     $q = array();
     foreach (array("title", "abstract", "collaborators") as $k) {
         $v = convert_to_utf8((string) get($pj, $k));
         if (!$old_pj || get($pj, $k) !== null && $v !== (string) get($old_pj, $k)) {
             $q[] = "{$k}='" . sqlq($v) . "'";
         }
     }
     if (!$old_pj || get($pj, "authors") !== null) {
         $autext = convert_to_utf8(self::author_information($pj));
         $old_autext = self::author_information($old_pj);
         if ($autext !== $old_autext || !$old_pj) {
             $q[] = "authorInformation='" . sqlq($autext) . "'";
         }
     }
     if ($Conf->submission_blindness() == Conf::BLIND_OPTIONAL && (!$old_pj || get($pj, "nonblind") !== null && !$pj->nonblind != !$old_pj->nonblind)) {
         $q[] = "blind=" . (get($pj, "nonblind") ? 0 : 1);
     }
     if (!$old_pj || get($pj, "submission") !== null) {
         $new_id = get($pj, "submission") ? $pj->submission->docid : 1;
         $old_id = $old_pj && get($old_pj, "submission") ? $old_pj->submission->docid : 1;
         if (!$old_pj || $new_id != $old_id) {
             $q[] = "paperStorageId={$new_id}";
         }
     }
     if (!$old_pj || get($pj, "final") !== null) {
         $new_id = get($pj, "final") ? $pj->final->docid : 0;
         $old_id = $old_pj && get($old_pj, "final") ? $old_pj->final->docid : 0;
         if (!$old_pj || $new_id != $old_id) {
             $q[] = "finalPaperStorageId={$new_id}";
         }
     }
     if (get($pj, "withdrawn") !== null || get($pj, "submitted") !== null || get($pj, "draft") !== null) {
         if (get($pj, "submitted") !== null) {
             $submitted = $pj->submitted;
         } else {
             if (get($pj, "draft") !== null) {
                 $submitted = !$pj->draft;
             } else {
                 if ($old_pj) {
                     $submitted = get($old_pj, "submitted_at") > 0;
                 } else {
                     $submitted = false;
                 }
             }
         }
         if (get($pj, "withdrawn")) {
             if (!$old_pj || !get($old_pj, "withdrawn")) {
                 $q[] = "timeWithdrawn=" . (get($pj, "withdrawn_at") ?: $Now);
                 $q[] = "timeSubmitted=" . ($submitted ? -100 : 0);
             } else {
                 if (get($old_pj, "submitted_at") > 0 !== $submitted) {
                     $q[] = "timeSubmitted=" . ($submitted ? -100 : 0);
                 }
             }
         } else {
             if ($submitted) {
                 if (!$old_pj || !get($old_pj, "submitted")) {
                     $q[] = "timeSubmitted=" . (get($pj, "submitted_at") ?: $Now);
                 }
                 if ($old_pj && get($old_pj, "withdrawn")) {
                     $q[] = "timeWithdrawn=0";
                 }
             } else {
                 if ($old_pj && (get($old_pj, "withdrawn") || get($old_pj, "submitted"))) {
                     $q[] = "timeSubmitted=0";
                     $q[] = "timeWithdrawn=0";
                 }
             }
         }
     }
     if (get($pj, "final_submitted") !== null) {
         if ($pj->final_submitted) {
             $time = get($pj, "final_submitted_at") ?: $Now;
         } else {
             $time = 0;
         }
         if (!$old_pj || get($old_pj, "final_submitted_at") != $time) {
             $q[] = "timeFinalSubmitted={$time}";
         }
     }
     if (!empty($q)) {
         if ($Conf->submission_blindness() == Conf::BLIND_NEVER) {
             $q[] = "blind=0";
         } else {
             if ($Conf->submission_blindness() != Conf::BLIND_OPTIONAL) {
                 $q[] = "blind=1";
             }
         }
         $joindoc = $old_joindoc = null;
         if (get($pj, "final")) {
             $joindoc = $pj->final;
             $old_joindoc = $old_pj ? get($old_pj, "final") : null;
         } else {
             if (get($pj, "submission")) {
                 $joindoc = $pj->submission;
                 $old_joindoc = $old_pj ? get($old_pj, "submission") : null;
             }
         }
         if ($joindoc && (!$old_joindoc || $old_joindoc->docid != $joindoc->docid) && get($joindoc, "size") && get($joindoc, "timestamp")) {
             $q[] = "size=" . $joindoc->size;
             $q[] = "mimetype='" . sqlq($joindoc->mimetype) . "'";
             $q[] = "sha1='" . sqlq($joindoc->sha1) . "'";
             $q[] = "timestamp=" . $joindoc->timestamp;
         } else {
             if (!$joindoc) {
                 $q[] = "size=0,mimetype='',sha1='',timestamp=0";
             }
         }
         if ($paperid) {
             $result = Dbl::qe_raw("update Paper set " . join(",", $q) . " where paperId={$paperid}");
             if ($result && $result->affected_rows === 0 && edb_nrows(Dbl::qe_raw("select paperId from Paper where paperId={$paperid}")) === 0) {
                 $result = Dbl::qe_raw("insert into Paper set paperId={$paperid}, " . join(",", $q));
             }
         } else {
             $result = Dbl::qe_raw("insert into Paper set " . join(",", $q));
             if (!$result || !($paperid = $pj->pid = $result->insert_id)) {
                 return $this->set_error_html(false, "Could not create paper.");
             }
             if (!empty($this->uploaded_documents)) {
                 Dbl::qe_raw("update PaperStorage set paperId={$paperid} where paperStorageId in (" . join(",", $this->uploaded_documents) . ")");
             }
         }
         // maybe update `papersub` settings
         $is_submitted = !get($pj, "withdrawn") && get($pj, "submitted");
         $was_submitted = $old_pj && !get($old_pj, "withdrawn") && get($old_pj, "submitted");
         if ($is_submitted != $was_submitted) {
             $Conf->update_papersub_setting($is_submitted);
         }
     }
     // update PaperTopics
     if (get($pj, "topics")) {
         $topics = self::topics_sql($pj, $paperid);
         $old_topics = self::topics_sql($old_pj, $paperid);
         if ($topics !== $old_topics) {
             $result = Dbl::qe_raw("delete from PaperTopic where paperId={$paperid}");
             if ($topics) {
                 $result = Dbl::qe_raw("insert into PaperTopic (topicId,paperId) values {$topics}");
             }
         }
     }
     // update PaperOption
     if (get($pj, "options")) {
         $options = convert_to_utf8(self::options_sql($pj, $paperid));
         $old_options = self::options_sql($old_pj, $paperid);
         if ($options !== $old_options) {
             $result = Dbl::qe("delete from PaperOption where paperId={$paperid} and optionId?a", array_keys($pj->parsed_options));
             if ($options) {
                 $result = Dbl::qe_raw("insert into PaperOption (paperId,optionId,value,data) values {$options}");
             }
         }
     }
     // update PaperConflict
     $conflict = $this->conflicts_array($pj, $old_pj);
     $old_conflict = $this->conflicts_array($old_pj, null);
     if (join(",", array_keys($conflict)) !== join(",", array_keys($old_conflict)) || join(",", array_values($conflict)) !== join(",", array_values($old_conflict))) {
         $q = array();
         foreach ($conflict as $email => $type) {
             $q[] = "'" . sqlq($email) . "'";
         }
         $ins = array();
         if (!empty($q)) {
             $result = Dbl::qe_raw("select contactId, email from ContactInfo where email in (" . join(",", $q) . ")");
             while ($row = edb_row($result)) {
                 $ins[] = "({$paperid},{$row['0']}," . $conflict[strtolower($row[1])] . ")";
             }
         }
         $result = Dbl::qe_raw("delete from PaperConflict where paperId={$paperid}");
         if (!empty($ins)) {
             $result = Dbl::qe_raw("insert into PaperConflict (paperId,contactId,conflictType) values " . join(",", $ins));
         }
     }
     return $paperid;
 }
Пример #7
0
if ($Qreq->delete && check_post()) {
    if ($newPaper) {
        $Conf->confirmMsg("Paper deleted.");
    } else {
        if (!$Me->privChair) {
            Conf::msg_error("Only the program chairs can permanently delete papers. Authors can withdraw papers, which is effectively the same.");
        } else {
            // mail first, before contact info goes away
            if (!$Me->privChair || $Qreq->doemail > 0) {
                HotCRPMailer::send_contacts("@deletepaper", $prow, array("reason" => (string) $Qreq->emailNote, "infoNames" => 1));
            }
            // XXX email self?
            $error = false;
            $tables = array('Paper', 'PaperStorage', 'PaperComment', 'PaperConflict', 'PaperReview', 'PaperReviewPreference', 'PaperTopic', 'PaperTag', "PaperOption");
            foreach ($tables as $table) {
                $result = Dbl::qe_raw("delete from {$table} where paperId={$prow->paperId}");
                $error |= $result == false;
            }
            if (!$error) {
                $Conf->confirmMsg("Paper #{$prow->paperId} deleted.");
                $Conf->update_papersub_setting(false);
                if ($prow->outcome > 0) {
                    $Conf->update_paperacc_setting(false);
                }
                $Me->log_activity("Deleted", $prow->paperId);
            }
            $prow = null;
            errorMsgExit("");
        }
    }
}
Пример #8
0
function assert_query($q, $b)
{
    $result = Dbl::qe_raw($q);
    xassert_eqq(join("\n", edb_first_columns($result)), $b);
}
 public function data()
 {
     global $Conf, $Me;
     // load data
     $paperIds = array_keys($this->papermap);
     $queryOptions = array("paperId" => $paperIds, "tags" => true);
     $this->fx->add_query_options($queryOptions, $Me);
     $this->fy->add_query_options($queryOptions, $Me);
     if ($this->fx->needs_review() || $this->fy->needs_review()) {
         $queryOptions["reviewOrdinals"] = true;
     }
     $result = Dbl::qe_raw($Conf->paperQuery($Me, $queryOptions));
     if ($this->type == self::CDF) {
         $data = $this->_cdf_data($result);
     } else {
         if ($this->type & self::BARCHART) {
             $data = $this->_combine_data($result);
         } else {
             $data = $this->_scatter_data($result);
         }
     }
     $this->_reviewer_reformat($data);
     $this->_revround_reformat($data);
     Dbl::free($result);
     return $data;
 }
Пример #10
0
 function change_grader($grader)
 {
     if (is_object($grader)) {
         $grader = $grader->contactId;
     }
     if ($this->pset->gitless_grades) {
         $q = Dbl::format_query("insert into ContactGrade (cid,pset,gradercid) values (?, ?, ?) on duplicate key update gradercid=values(gradercid)", $this->user->contactId, $this->pset->psetid, $grader);
     } else {
         assert(!!$this->commit);
         if (!$this->repo_grade || !$this->repo_grade->gradehash) {
             $q = Dbl::format_query("insert into RepositoryGrade set repoid=?, pset=?, gradehash=?, gradercid=?, placeholder=0 on duplicate key update gradehash=values(gradehash), gradercid=values(gradercid), placeholder=0", $this->repo->repoid, $this->pset->psetid, $this->commit ?: null, $grader);
         } else {
             $q = Dbl::format_query("update RepositoryGrade set gradehash=?, gradercid=?, placeholder=0 where repoid=? and pset=? and gradehash=?", $this->commit ?: $this->repo_grade->gradehash, $grader, $this->repo->repoid, $this->pset->psetid, $this->repo_grade->gradehash);
         }
         $this->update_commit_info(array("gradercid" => $grader));
     }
     if ($q) {
         Dbl::qe_raw($q);
     }
     $this->grade = $this->repo_grade = false;
 }
Пример #11
0
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;
}
Пример #12
0
 static function update_review_delegation($pid, $cid, $direction)
 {
     if ($direction > 0) {
         Dbl::qe_raw("update PaperReview set reviewNeedsSubmit=-1 where paperId={$pid} and reviewType=" . REVIEW_SECONDARY . " and contactId={$cid} and reviewSubmitted is null and reviewNeedsSubmit=1");
     } else {
         if ($direction <= 0) {
             $row = Dbl::fetch_first_row("select sum(contactId={$cid} and reviewType=" . REVIEW_SECONDARY . " and reviewSubmitted is null), sum(reviewType<" . REVIEW_SECONDARY . " and requestedBy={$cid} and reviewSubmitted is not null), sum(reviewType<" . REVIEW_SECONDARY . " and requestedBy={$cid}) from PaperReview where paperId={$pid}");
             if ($row && $row[0]) {
                 $rns = $row[1] ? 0 : ($row[2] ? -1 : 1);
                 if ($direction == 0 || $rns != 0) {
                     Dbl::qe("update PaperReview set reviewNeedsSubmit={$rns} where paperId={$pid} and contactId={$cid} and reviewSubmitted is null");
                 }
             }
         }
     }
 }
Пример #13
0
 public function notify($notifytype, $callback, $contact)
 {
     global $Conf;
     $q = "select ContactInfo.contactId, firstName, lastName, email,\n                password, contactTags, roles, defaultWatch,\n                PaperReview.reviewType myReviewType,\n                PaperReview.reviewSubmitted myReviewSubmitted,\n                PaperReview.reviewNeedsSubmit myReviewNeedsSubmit,\n                conflictType, watch, preferredEmail, disabled\n        from ContactInfo\n        left join PaperConflict on (PaperConflict.paperId={$this->paperId} and PaperConflict.contactId=ContactInfo.contactId)\n        left join PaperWatch on (PaperWatch.paperId={$this->paperId} and PaperWatch.contactId=ContactInfo.contactId)\n        left join PaperReview on (PaperReview.paperId={$this->paperId} and PaperReview.contactId=ContactInfo.contactId)\n        left join PaperComment on (PaperComment.paperId={$this->paperId} and PaperComment.contactId=ContactInfo.contactId)\n        where watch is not null\n        or conflictType>=" . CONFLICT_AUTHOR . "\n        or reviewType is not null or commentId is not null\n        or (defaultWatch & " . ($notifytype << WATCHSHIFT_ALL) . ")!=0";
     if ($this->managerContactId > 0) {
         $q .= " or ContactInfo.contactId=" . $this->managerContactId;
     }
     $q .= " order by conflictType";
     $result = Dbl::qe_raw($q);
     $watchers = array();
     $lastContactId = 0;
     while ($result && ($row = Contact::fetch($result))) {
         if ($row->contactId == $lastContactId || $contact && $row->contactId == $contact->contactId || Contact::is_anonymous_email($row->email)) {
             continue;
         }
         $lastContactId = $row->contactId;
         if ($row->watch && $row->watch & $notifytype << WATCHSHIFT_EXPLICIT) {
             if (!($row->watch & $notifytype << WATCHSHIFT_NORMAL)) {
                 continue;
             }
         } else {
             if (!($row->defaultWatch & ($notifytype << WATCHSHIFT_NORMAL | $notifytype << WATCHSHIFT_ALL))) {
                 continue;
             }
         }
         $watchers[$row->contactId] = $row;
     }
     // save my current contact info map -- we are replacing it with another
     // map that lacks review token information and so forth
     $cimap = $this->replace_contact_info_map(null);
     foreach ($watchers as $minic) {
         $this->assign_contact_info($minic, $minic->contactId);
         call_user_func($callback, $this, $minic);
     }
     $this->replace_contact_info_map($cimap);
 }
Пример #14
0
 function run(Contact $user, $qreq, $ssel)
 {
     global $Conf;
     $type = $this->islead ? "lead" : "shepherd";
     $result = Dbl::qe_raw($Conf->paperQuery($user, array("paperId" => $ssel->selection(), "reviewerName" => $type)));
     $texts = array();
     while ($row = PaperInfo::fetch($result, $user)) {
         if ($row->reviewEmail && ($this->islead ? $user->can_view_lead($row, true) : $user->can_view_shepherd($row, true))) {
             arrayappend($texts[$row->paperId], [$row->paperId, $row->title, $row->reviewFirstName, $row->reviewLastName, $row->reviewEmail]);
         }
     }
     downloadCSV($ssel->reorder($texts), array("paper", "title", "first", "last", "{$type}email"), "{$type}s");
 }
Пример #15
0
 function _search()
 {
     global $Conf;
     if ($this->_matches === false) {
         return false;
     }
     assert($this->_matches === null);
     if ($this->limitName === "x") {
         $this->_matches = array();
         return true;
     }
     // parse and clean the query
     $qe = $this->_searchQueryType($this->q);
     //Conf::msg_info(Ht::pre_text(var_export($qe, true)));
     if (!$qe) {
         $qe = new SearchTerm("t");
     }
     // apply complex limiters (only current example: "acc" for non-chairs)
     $limit = $this->limitName;
     if ($limit === "acc" && !$this->privChair) {
         $qe = SearchTerm::make_op("and", array($qe, $this->_searchQueryWord("dec:yes", false)));
     }
     // apply review rounds (top down, needs separate step)
     if ($this->reviewAdjust) {
         $qe = $this->_query_adjust_reviews($qe, null);
         if ($this->_reviewAdjustError) {
             $this->warn("Unexpected use of “round:” or “rate:” ignored.  Stick to the basics, such as “re:reviewername round:roundname”.");
         }
     }
     //Conf::msg_info(Ht::pre_text(var_export($qe, true)));
     // collect clauses into tables, columns, and filters
     $sqi = new SearchQueryInfo();
     $sqi->add_table("Paper");
     $sqi->add_column("paperId", "Paper.paperId");
     // always include columns needed by rights machinery
     $sqi->add_column("timeSubmitted", "Paper.timeSubmitted");
     $sqi->add_column("timeWithdrawn", "Paper.timeWithdrawn");
     $sqi->add_column("outcome", "Paper.outcome");
     $filters = array();
     $this->_clauseTermSet($qe, $sqi, $filters);
     //Conf::msg_info(Ht::pre_text(var_export($filters, true)));
     // status limitation parts
     if ($limit === "rable") {
         $limitcontact = $this->_reviewer_fixed ? $this->reviewer() : $this->contact;
         if ($limitcontact->can_accept_review_assignment_ignore_conflict(null)) {
             $limit = $Conf->can_pc_see_all_submissions() ? "act" : "s";
         } else {
             if (!$limitcontact->isPC) {
                 $limit = "r";
             }
         }
     }
     if ($limit === "s" || $limit === "req" || $limit === "acc" || $limit === "und" || $limit === "unm" || $limit === "rable" && !$Conf->can_pc_see_all_submissions()) {
         $filters[] = "Paper.timeSubmitted>0";
     } else {
         if ($limit === "act" || $limit === "r" || $limit === "rable") {
             $filters[] = "Paper.timeWithdrawn<=0";
         } else {
             if ($limit === "unsub") {
                 $filters[] = "(Paper.timeSubmitted<=0 and Paper.timeWithdrawn<=0)";
             } else {
                 if ($limit === "lead") {
                     $filters[] = "Paper.leadContactId=" . $this->cid;
                 } else {
                     if ($limit === "manager") {
                         if ($this->privChair) {
                             $filters[] = "(Paper.managerContactId=" . $this->cid . " or Paper.managerContactId=0)";
                         } else {
                             $filters[] = "Paper.managerContactId=" . $this->cid;
                         }
                         $filters[] = "Paper.timeSubmitted>0";
                     }
                 }
             }
         }
     }
     // decision limitation parts
     if ($limit === "acc") {
         $filters[] = "Paper.outcome>0";
     } else {
         if ($limit === "und") {
             $filters[] = "Paper.outcome=0";
         }
     }
     // other search limiters
     if ($limit === "a") {
         $filters[] = $this->contact->actAuthorSql("PaperConflict");
         $this->needflags |= self::F_AUTHOR;
     } else {
         if ($limit === "r") {
             $filters[] = "MyReview.reviewType is not null";
             $this->needflags |= self::F_REVIEWER;
         } else {
             if ($limit === "ar") {
                 $filters[] = "(" . $this->contact->actAuthorSql("PaperConflict") . " or (Paper.timeWithdrawn<=0 and MyReview.reviewType is not null))";
                 $this->needflags |= self::F_AUTHOR | self::F_REVIEWER;
             } else {
                 if ($limit === "rout") {
                     $filters[] = "MyReview.reviewNeedsSubmit!=0";
                     $this->needflags |= self::F_REVIEWER;
                 } else {
                     if ($limit === "revs") {
                         $sqi->add_table("Limiter", array("join", "PaperReview"));
                     } else {
                         if ($limit === "req") {
                             $sqi->add_table("Limiter", array("join", "PaperReview", "Limiter.requestedBy={$this->cid} and Limiter.reviewType=" . REVIEW_EXTERNAL));
                         } else {
                             if ($limit === "unm") {
                                 $filters[] = "Paper.managerContactId=0";
                             }
                         }
                     }
                 }
             }
         }
     }
     // add common tables: conflicts, my own review, paper blindness
     if ($this->needflags & (self::F_MANAGER | self::F_NONCONFLICT | self::F_AUTHOR)) {
         $sqi->add_table("PaperConflict", array("left join", "PaperConflict", "PaperConflict.contactId={$this->cid}"));
         $sqi->add_column("conflictType", "PaperConflict.conflictType");
     }
     if ($this->needflags & self::F_REVIEWER) {
         if ($Conf->submission_blindness() == Conf::BLIND_OPTIONAL) {
             $sqi->add_column("paperBlind", "Paper.blind");
         }
         $qb = "";
         if ($tokens = $this->contact->review_tokens()) {
             $qb = " or MyReview.reviewToken in (" . join(",", $tokens) . ")";
         }
         $sqi->add_table("MyReview", array("left join", "PaperReview", "(MyReview.contactId={$this->cid}{$qb})"));
         $sqi->add_column("myReviewType", "MyReview.reviewType");
         $sqi->add_column("myReviewNeedsSubmit", "MyReview.reviewNeedsSubmit");
         $sqi->add_column("myReviewSubmitted", "MyReview.reviewSubmitted");
     }
     // check for annotated order
     $order_anno_tag = null;
     if ($qe->type !== "then" && ($sort = $qe->get_float("sort")) && ($tag = self::_check_sort_order_anno($sort))) {
         $dt = TagInfo::make_defined_tag($tag);
         if (count($dt->order_anno_list())) {
             $order_anno_tag = $dt;
         }
     }
     // add permissions tables if we will filter the results
     $need_filter = $this->needflags & self::F_XVIEW || $Conf->has_tracks() || $qe->type === "then" || $qe->get_float("heading") || $limit === "rable" || $order_anno_tag;
     if ($need_filter) {
         $sqi->add_rights_columns();
         if ($Conf->submission_blindness() == Conf::BLIND_OPTIONAL) {
             $sqi->add_column("paperBlind", "Paper.blind");
         }
     }
     // XXX some of this should be shared with paperQuery
     if ($need_filter && $Conf->has_track_tags() || get($this->_query_options, "tags") || $order_anno_tag) {
         $sqi->add_column("paperTags", "(select group_concat(' ', tag, '#', tagIndex separator '') from PaperTag where PaperTag.paperId=Paper.paperId)");
     }
     if (get($this->_query_options, "scores") || get($this->_query_options, "reviewTypes") || get($this->_query_options, "reviewContactIds")) {
         $j = "group_concat(contactId order by reviewId) reviewContactIds";
         $sqi->add_column("reviewContactIds", "R_submitted.reviewContactIds");
         if (get($this->_query_options, "reviewTypes")) {
             $j .= ", group_concat(reviewType order by reviewId) reviewTypes";
             $sqi->add_column("reviewTypes", "R_submitted.reviewTypes");
         }
         foreach (get($this->_query_options, "scores") ?: array() as $f) {
             $j .= ", group_concat({$f} order by reviewId) {$f}Scores";
             $sqi->add_column("{$f}Scores", "R_submitted.{$f}Scores");
         }
         $sqi->add_table("R_submitted", array("left join", "(select paperId, {$j} from PaperReview where reviewSubmitted>0 group by paperId)"));
     }
     // create query
     $q = "select ";
     foreach ($sqi->columns as $colname => $value) {
         $q .= $value . " " . $colname . ", ";
     }
     $q = substr($q, 0, strlen($q) - 2) . "\n    from ";
     foreach ($sqi->tables as $tabname => $value) {
         if (!$value) {
             $q .= $tabname;
         } else {
             $joiners = array("{$tabname}.paperId=Paper.paperId");
             for ($i = 2; $i < count($value); ++$i) {
                 $joiners[] = "(" . $value[$i] . ")";
             }
             $q .= "\n    " . $value[0] . " " . $value[1] . " as " . $tabname . " on (" . join("\n        and ", $joiners) . ")";
         }
     }
     if (count($filters)) {
         $q .= "\n    where " . join("\n        and ", $filters);
     }
     $q .= "\n    group by Paper.paperId";
     //Conf::msg_info(Ht::pre_text_wrap($q));
     // actually perform query
     $result = Dbl::qe_raw($q);
     if (!$result) {
         return $this->_matches = false;
     }
     $this->_matches = array();
     // correct query, create thenmap, groupmap, highlightmap
     $need_then = $qe->type === "then";
     $this->thenmap = null;
     if ($need_then && $qe->nthen > 1) {
         $this->thenmap = array();
     }
     $this->highlightmap = array();
     if ($need_filter) {
         $tag_order = [];
         while ($row = PaperInfo::fetch($result, $this->cid)) {
             if (!$this->contact->can_view_paper($row) || $limit === "rable" && !$limitcontact->can_accept_review_assignment_ignore_conflict($row)) {
                 $x = false;
             } else {
                 if ($need_then) {
                     $x = false;
                     for ($i = 0; $i < $qe->nthen && $x === false; ++$i) {
                         if ($this->_clauseTermCheck($qe->value[$i], $row)) {
                             $x = $i;
                         }
                     }
                 } else {
                     $x = !!$this->_clauseTermCheck($qe, $row);
                 }
             }
             if ($x === false) {
                 continue;
             }
             $this->_matches[] = $row->paperId;
             if ($this->thenmap !== null) {
                 $this->thenmap[$row->paperId] = $x;
             }
             if ($need_then) {
                 for ($j = $qe->nthen; $j < count($qe->value); ++$j) {
                     if ($this->_clauseTermCheck($qe->value[$j], $row) && $qe->highlights[$j - $qe->nthen] & 1 << $x) {
                         $this->highlightmap[$row->paperId] = $qe->highlight_types[$j - $qe->nthen] . "highlight";
                         break;
                     }
                 }
             }
             if ($order_anno_tag) {
                 if ($row->has_viewable_tag($order_anno_tag->tag, $this->contact)) {
                     $tag_order[] = [$row->paperId, $row->tag_value($order_anno_tag->tag)];
                 } else {
                     $tag_order[] = [$row->paperId, TAG_INDEXBOUND];
                 }
             }
         }
     } else {
         while ($row = $result->fetch_object()) {
             $this->_matches[] = (int) $row->paperId;
         }
     }
     Dbl::free($result);
     // add deleted papers explicitly listed by number (e.g. action log)
     if ($this->_allow_deleted) {
         $this->_add_deleted_papers($qe);
     }
     // view and sort information
     $this->viewmap = $qe->get_float("view", array());
     $this->sorters = array();
     $this->_add_sorters($qe, null);
     if ($qe->type === "then") {
         for ($i = 0; $i < $qe->nthen; ++$i) {
             $this->_add_sorters($qe->value[$i], $this->thenmap ? $i : null);
         }
     }
     $this->groupmap = [];
     if ($qe->type === "then") {
         for ($i = 0; $i < $qe->nthen; ++$i) {
             $h = $qe->value[$i]->get_float("heading");
             $this->groupmap[$i] = (object) ["heading" => $h, "annoFormat" => 0];
         }
     } else {
         if ($h = $qe->get_float("heading")) {
             $this->groupmap[0] = (object) ["heading" => $h, "annoFormat" => 0];
         } else {
             if ($order_anno_tag) {
                 $this->_assign_order_anno($order_anno_tag, $tag_order);
                 $this->is_order_anno = $order_anno_tag->tag;
             }
         }
     }
     // extract regular expressions and set _reviewer if the query is
     // about exactly one reviewer, and warn about contradictions
     $contradictions = array();
     $this->_queryExtractInfo($qe, true, false, $contradictions);
     foreach ($contradictions as $contradiction => $garbage) {
         $this->warn($contradiction);
     }
     // set $this->matchPreg from $this->regex
     if (!$this->overrideMatchPreg) {
         $this->matchPreg = array();
         foreach (array("ti" => "title", "au" => "authorInformation", "ab" => "abstract", "co" => "collaborators") as $k => $v) {
             if (isset($this->regex[$k]) && count($this->regex[$k])) {
                 $a = $b = array();
                 foreach ($this->regex[$k] as $x) {
                     $a[] = $x->preg_utf8;
                     if (isset($x->preg_raw)) {
                         $b[] = $x->preg_raw;
                     }
                 }
                 $x = (object) array("preg_utf8" => join("|", $a));
                 if (count($a) == count($b)) {
                     $x->preg_raw = join("|", $b);
                 }
                 $this->matchPreg[$v] = $x;
             }
         }
     }
     return true;
 }
Пример #16
0
 function save_review($req, $rrow, $prow, $contact, &$tf = null)
 {
     global $Conf, $Opt;
     $newsubmit = @$req["ready"] && !@$req["unready"] && (!$rrow || !$rrow->reviewSubmitted);
     $submit = $newsubmit || $rrow && $rrow->reviewSubmitted;
     $admin = $contact->allow_administer($prow);
     if (!$contact->timeReview($prow, $rrow) && (!isset($req['override']) || !$admin)) {
         return Conf::msg_error("The <a href='" . hoturl("deadlines") . "'>deadline</a> for entering this review has passed." . ($admin ? "  Select the “Override deadlines” checkbox and try again if you really want to override the deadline." : ""));
     }
     $q = array();
     $diff_view_score = VIEWSCORE_FALSE;
     $wc = 0;
     foreach ($this->forder as $field => $f) {
         if (isset($req[$field]) && (!$f->round_mask || $f->is_round_visible($rrow))) {
             $fval = $req[$field];
             if ($f->has_options) {
                 if ($f->parse_is_empty($fval)) {
                     $fval = 0;
                 } else {
                     if (!($fval = $f->parse_value($fval, false))) {
                         continue;
                     }
                 }
             } else {
                 $fval = rtrim($fval);
                 if ($fval != "") {
                     $fval .= "\n";
                 }
                 // Check for valid UTF-8; re-encode from Windows-1252 or Mac OS
                 $fval = convert_to_utf8($fval);
                 if ($f->include_word_count()) {
                     $wc += count_words($fval);
                 }
             }
             if ($rrow && strcmp($rrow->{$field}, $fval) != 0 && strcmp(cleannl($rrow->{$field}), cleannl($fval)) != 0) {
                 $diff_view_score = max($diff_view_score, $f->view_score);
             }
             $q[] = "{$field}='" . sqlq($fval) . "'";
         }
     }
     // get the current time
     $now = time();
     if ($rrow && $rrow->reviewModified && $rrow->reviewModified > $now) {
         $now = $rrow->reviewModified + 1;
     }
     // potentially assign review ordinal (requires table locking since
     // mySQL is stupid)
     $locked = false;
     if ($newsubmit) {
         $diff_view_score = max($diff_view_score, VIEWSCORE_AUTHOR);
         $q[] = "reviewSubmitted={$now}, reviewNeedsSubmit=0";
         if (!$rrow || !$rrow->reviewOrdinal) {
             $result = $Conf->qe("lock tables PaperReview write");
             if (!$result) {
                 return $result;
             }
             $locked = true;
             $result = $Conf->qe("select coalesce(max(reviewOrdinal), 0) from PaperReview where paperId={$prow->paperId} group by paperId");
             if ($result) {
                 $crow = edb_row($result);
                 $q[] = "reviewOrdinal=coalesce(reviewOrdinal, " . ($crow[0] + 1) . ")";
             }
             Dbl::free($result);
             $q[] = "timeDisplayed={$now}";
         }
     }
     // check whether used a review token
     $usedReviewToken = $contact->review_token_cid($prow, $rrow);
     // blind? reviewer type? edit version?
     $reviewBlind = $Conf->is_review_blind(!!@$req["blind"]);
     if ($rrow && $reviewBlind != $rrow->reviewBlind) {
         $diff_view_score = max($diff_view_score, VIEWSCORE_ADMINONLY);
     }
     $q[] = "reviewBlind=" . ($reviewBlind ? 1 : 0);
     if ($rrow && $rrow->reviewType == REVIEW_EXTERNAL && $contact->contactId == $rrow->contactId && $contact->isPC && !$usedReviewToken) {
         $q[] = "reviewType=" . REVIEW_PC;
     }
     if ($rrow && $diff_view_score > VIEWSCORE_FALSE && isset($req["version"]) && ctype_digit($req["version"]) && $req["version"] > defval($rrow, "reviewEditVersion")) {
         $q[] = "reviewEditVersion=" . ($req["version"] + 0);
     }
     if ($diff_view_score > VIEWSCORE_FALSE && $Conf->sversion >= 98) {
         $q[] = "reviewWordCount=" . $wc;
     }
     if (isset($req["reviewFormat"]) && $Conf->sversion >= 104 && @$Opt["formatInfo"]) {
         $fmt = null;
         foreach ($Opt["formatInfo"] as $k => $f) {
             if (@$f["name"] && strcasecmp($f["name"], $req["reviewFormat"]) == 0) {
                 $fmt = (int) $k;
             }
         }
         if (!$fmt && $req["reviewFormat"] && preg_match('/\\A(?:plain\\s*)?(?:text)?\\z/i', $f["reviewFormat"])) {
             $fmt = 0;
         }
         $q[] = "reviewFormat=" . ($fmt === null ? "null" : $fmt);
     }
     // notification
     $notification_bound = $now - 10800;
     $notify = $notify_author = false;
     if (!$rrow || $diff_view_score > VIEWSCORE_FALSE) {
         $q[] = "reviewModified=" . $now;
         // do not notify on updates within 3 hours
         if ($submit && $diff_view_score > VIEWSCORE_ADMINONLY) {
             if (!$rrow || !$rrow->reviewNotified || $rrow->reviewNotified < $notification_bound) {
                 $q[] = $notify = "reviewNotified=" . $now;
             }
             if ((!$rrow || !$rrow->reviewAuthorNotified || $rrow->reviewAuthorNotified < $notification_bound) && $diff_view_score >= VIEWSCORE_AUTHOR && Contact::can_some_author_view_submitted_review($prow)) {
                 $q[] = $notify_author = "reviewAuthorNotified=" . $now;
             }
         }
     }
     // actually affect database
     if ($rrow) {
         $result = $Conf->qe("update PaperReview set " . join(", ", $q) . " where reviewId={$rrow->reviewId}");
         $reviewId = $rrow->reviewId;
         $contactId = $rrow->contactId;
     } else {
         $result = Dbl::qe_raw("insert into PaperReview set paperId={$prow->paperId}, contactId={$contact->contactId}, reviewType=" . REVIEW_PC . ", requestedBy={$contact->contactId}, reviewRound=" . $Conf->current_round() . ", " . join(", ", $q));
         $reviewId = $result ? $result->insert_id : null;
         $contactId = $contact->contactId;
     }
     // unlock tables even if problem
     if ($locked) {
         $Conf->qe("unlock tables");
     }
     if (!$result) {
         return $result;
     }
     // update caches
     Contact::update_rights();
     // look up review ID
     if (!$reviewId) {
         return $reviewId;
     }
     $req['reviewId'] = $reviewId;
     // log updates -- but not if review token is used
     if (!$usedReviewToken && $diff_view_score > VIEWSCORE_FALSE) {
         $text = "Review {$reviewId} ";
         if ($rrow && $contact->contactId != $rrow->contactId) {
             $text .= "by {$rrow->email} ";
         }
         $text .= $newsubmit ? "submitted" : ($submit ? "updated" : "saved draft");
         $contact->log_activity($text, $prow);
     }
     // potentially email chair, reviewers, and authors
     if ($submit) {
         $rrow = $Conf->reviewRow(["reviewId" => $reviewId]);
     }
     if ($submit && ($notify || $notify_author) && $rrow) {
         $tmpl = $newsubmit ? "@reviewsubmit" : "@reviewupdate";
         $submitter = $contact;
         if ($contactId != $submitter->contactId) {
             $submitter = Contact::find_by_id($contactId);
         }
         // construct mail
         $this->mailer_info = array("template" => $tmpl, "rrow" => $rrow, "reviewer_contact" => $submitter, "reviewNumber" => $prow->paperId . unparseReviewOrdinal($rrow->reviewOrdinal), "check_function" => "HotCRPMailer::check_can_view_review", "diff_view_score" => $diff_view_score);
         $this->mailer_preps = array();
         if ($Conf->timeEmailChairAboutReview()) {
             HotCRPMailer::send_manager($tmpl, $prow, $this->mailer_info);
         }
         $prow->notify(WATCHTYPE_REVIEW, array($this, "review_watch_callback"), $contact);
         if (count($this->mailer_preps)) {
             HotCRPMailer::send_combined_preparations($this->mailer_preps);
         }
         unset($this->mailer_info, $this->mailer_preps);
     }
     // if external, forgive the requestor from finishing their review
     if ($rrow && $rrow->reviewType < REVIEW_SECONDARY && $rrow->requestedBy && $submit) {
         $Conf->q("update PaperReview set reviewNeedsSubmit=0 where paperId={$prow->paperId} and contactId={$rrow->requestedBy} and reviewType=" . REVIEW_SECONDARY . " and reviewSubmitted is null");
     }
     if ($tf !== null) {
         $what = "#{$prow->paperId}" . ($rrow && $rrow->reviewSubmitted ? unparseReviewOrdinal($rrow->reviewOrdinal) : "");
         if ($newsubmit) {
             $tf["newlySubmitted"][] = $what;
         } else {
             if ($diff_view_score > VIEWSCORE_FALSE && $submit) {
                 $tf["updated"][] = $what;
             } else {
                 if ($diff_view_score > VIEWSCORE_FALSE) {
                     $tf["savedDraft"][] = $what;
                 } else {
                     $tf["unchanged"][] = $what;
                 }
             }
         }
         if ($notify_author) {
             $tf["authorNotified"][] = $what;
         }
     }
     return $result;
 }
Пример #17
0
function refuseReview()
{
    global $Conf, $Opt, $Me, $prow, $paperTable;
    Dbl::qe_raw("lock tables PaperReview write, PaperReviewRefused write");
    $rrow = $paperTable->editrrow;
    $hadToken = defval($rrow, "reviewToken", 0) != 0;
    $result = Dbl::qe("delete from PaperReview where reviewId={$rrow->reviewId}");
    if (!$result) {
        return;
    }
    $reason = defval($_REQUEST, "reason", "");
    if ($reason == "Optional explanation") {
        $reason = "";
    }
    $result = Dbl::qe("insert into PaperReviewRefused set paperId={$rrow->paperId}, contactId={$rrow->contactId}, requestedBy={$rrow->requestedBy}, reason=?", trim($reason));
    if (!$result) {
        return;
    }
    // now the requester must potentially complete their review
    if ($rrow->reviewType < REVIEW_SECONDARY && $rrow->requestedBy > 0) {
        Contact::update_review_delegation($rrow->paperId, $rrow->requestedBy, -1);
    }
    Dbl::qe_raw("unlock tables");
    // send confirmation email
    $Requester = Contact::find_by_id($rrow->requestedBy);
    $reqprow = $Conf->paperRow($prow->paperId, $rrow->requestedBy);
    HotCRPMailer::send_to($Requester, "@refusereviewrequest", $reqprow, array("reviewer_contact" => $rrow, "reason" => $reason));
    // confirmation message
    $Conf->confirmMsg("The request that you review paper #{$prow->paperId} has been removed.  Mail was sent to the person who originally requested the review.");
    if ($hadToken) {
        $Conf->update_rev_tokens_setting(true);
    }
    $prow = null;
    confHeader();
    $Conf->footer();
    exit;
}
Пример #18
0
 static function settags_api($user, $qreq, $prow)
 {
     global $Conf;
     if ($qreq->cancelsettags) {
         json_exit(["ok" => true]);
     }
     if ($prow && !$user->can_view_paper($prow)) {
         json_exit(["ok" => false, "error" => "No such paper."]);
     }
     // save tags using assigner
     $x = array("paper,action,tag");
     if ($prow) {
         if (isset($qreq->tags)) {
             $x[] = "{$prow->paperId},tag,all#clear";
             foreach (TagInfo::split($qreq->tags) as $t) {
                 $x[] = "{$prow->paperId},tag," . CsvGenerator::quote($t);
             }
         }
         foreach (TagInfo::split((string) $qreq->addtags) as $t) {
             $x[] = "{$prow->paperId},tag," . CsvGenerator::quote($t);
         }
         foreach (TagInfo::split((string) $qreq->deltags) as $t) {
             $x[] = "{$prow->paperId},tag," . CsvGenerator::quote($t . "#clear");
         }
     } else {
         if (isset($qreq->tagassignment)) {
             $pids = [];
             $pid = -1;
             foreach (preg_split('/[\\s,]+/', $qreq->tagassignment) as $w) {
                 if ($w !== "" && ctype_digit($w)) {
                     $pid = intval($w);
                 } else {
                     if ($w !== "" && $pid > 0) {
                         $x[] = "{$pid},tag," . CsvGenerator::quote($w);
                         $pids[$pid] = true;
                     }
                 }
             }
         }
     }
     $assigner = new AssignmentSet($user, $user->is_admin_force());
     $assigner->parse(join("\n", $x));
     $error = join("<br />", $assigner->errors_html());
     $ok = $assigner->execute();
     // exit
     if ($ok && $prow) {
         $prow->load_tags();
         $treport = self::tagreport($user, $prow);
         if ($treport->warnings) {
             $Conf->warnMsg(join("<br>", $treport->warnings));
         }
         $taginfo = (object) ["ok" => true, "pid" => $prow->paperId];
         $prow->add_tag_info_json($taginfo, $user);
         json_exit($taginfo, true);
     } else {
         if ($ok) {
             $p = [];
             $result = Dbl::qe_raw($Conf->paperQuery($user, ["paperId" => array_keys($pids), "tags" => true]));
             while ($prow = PaperInfo::fetch($result, $user)) {
                 $p[$prow->paperId] = (object) [];
                 $prow->add_tag_info_json($p[$prow->paperId], $user);
             }
             json_exit(["ok" => true, "p" => $p]);
         } else {
             json_exit(["ok" => false, "error" => $error], true);
         }
     }
 }
 public function run_clear($reviewtype)
 {
     $papers = array_fill_keys($this->papersel, 1);
     if ($reviewtype == REVIEW_PRIMARY || $reviewtype == REVIEW_SECONDARY || $reviewtype == REVIEW_PC) {
         $q = "select paperId, contactId from PaperReview where reviewType=" . $reviewtype;
         $action = "noreview";
     } else {
         if ($reviewtype === "conflict") {
             $q = "select paperId, contactId from PaperConflict where conflictType>0 and conflictType<" . CONFLICT_AUTHOR;
             $action = "noconflict";
         } else {
             if ($reviewtype === "lead" || $reviewtype === "shepherd") {
                 $q = "select paperId, {$reviewtype}ContactId from Paper where {$reviewtype}ContactId!=0";
                 $action = "no" . $reviewtype;
             } else {
                 return false;
             }
         }
     }
     $this->ass = array("paper,action,email");
     $result = Dbl::qe_raw($q);
     while ($row = edb_row($result)) {
         if (!isset($papers[$row[0]]) || !isset($this->pcm[$row[1]])) {
             continue;
         }
         $this->ass[] = "{$row['0']},{$action}," . $this->pcm[$row[1]]->email;
     }
     Dbl::free($result);
 }
Пример #20
0
 function run(Contact $user, $qreq, $ssel)
 {
     global $Conf;
     $result = Dbl::qe_raw($Conf->paperQuery($user, array("paperId" => $ssel->selection(), "topics" => 1)));
     $texts = array();
     $tmap = $Conf->topic_map();
     while ($row = PaperInfo::fetch($result, $user)) {
         if ($user->can_view_paper($row)) {
             $out = array();
             foreach ($row->topics() as $t) {
                 $out[] = [$row->paperId, $row->title, $tmap[$t]];
             }
             if (!count($out)) {
                 $out[] = [$row->paperId, $row->title, "<none>"];
             }
             arrayappend($texts[$row->paperId], $out);
         }
     }
     downloadCSV($ssel->reorder($texts), array("paper", "title", "topic"), "topics");
 }
Пример #21
0
 function qe($query)
 {
     return Dbl::qe_raw($this->dblink, $query);
 }
Пример #22
0
    reconfig();
}
// check global system settings
if ($Me->privChair) {
    require_once "adminhome.php";
}
// Enable users
if ($Me->privChair && check_post() && isset($_GET["enable_user"])) {
    if ($_GET["enable_user"] == "college") {
        $users = edb_first_columns(Dbl::qe_raw("select contactId from ContactInfo where (roles&" . Contact::ROLE_PCLIKE . ")=0 and not extension"));
    } else {
        if ($_GET["enable_user"] == "extension") {
            $users = edb_first_columns(Dbl::qe_raw("select contactId from ContactInfo where (roles&" . Contact::ROLE_PCLIKE . ")=0 and extension"));
        } else {
            if ($_GET["enable_user"] == "pc") {
                $users = edb_first_columns(Dbl::qe_raw("select contactId from ContactInfo where (roles&" . Contact::ROLE_PC . ")!=0"));
            } else {
                $users = edb_first_columns(Dbl::qe("select contactId from ContactInfo where email like ?", $_GET["enable_user"]));
            }
        }
    }
    if (!count($users)) {
        $Conf->warnMsg("No users match.");
    } else {
        UserActions::enable($users, $Me);
        redirectSelf();
    }
}
$title = !$Me->is_empty() && !isset($_REQUEST["signin"]) ? "Home" : "Sign in";
$Conf->header($title, "home");
$xsep = " <span class='barsep'>&nbsp;|&nbsp;</span> ";
Пример #23
0
 public function fetch_prows($pids)
 {
     global $Conf;
     $pids = is_array($pids) ? $pids : array($pids);
     $fetch_pids = array();
     foreach ($pids as $p) {
         if (!isset($this->prows[$p])) {
             $fetch_pids[] = $p;
         }
     }
     if (count($fetch_pids)) {
         $q = $Conf->paperQuery($this->contact, array("paperId" => $fetch_pids));
         $result = Dbl::qe_raw($q);
         while ($result && ($prow = PaperInfo::fetch($result, $this->contact))) {
             $this->prows[$prow->paperId] = $prow;
         }
         Dbl::free($result);
     }
 }
 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];
 }
Пример #25
0
     echo $sep, '<a href="', hoturl("offline"), '">Offline reviewing</a>';
     $sep = $xsep;
 }
 if ($Me->is_requester()) {
     echo $sep, '<a href="', hoturl("mail", "monreq=1"), '">Monitor external reviews</a>';
     $sep = $xsep;
 }
 if ($Conf->setting("rev_tokens")) {
     echo $sep;
     reviewTokenGroup(false);
     $sep = $xsep;
 }
 if ($myrow && $Conf->setting("rev_ratings") != REV_RATINGS_NONE) {
     $badratings = PaperSearch::unusableRatings($Me->privChair, $Me->contactId);
     $qx = count($badratings) ? " and not (PaperReview.reviewId in (" . join(",", $badratings) . "))" : "";
     $result = Dbl::qe_raw("select rating, count(PaperReview.reviewId) from PaperReview join ReviewRating on (PaperReview.contactId={$Me->contactId} and PaperReview.reviewId=ReviewRating.reviewId{$qx}) group by rating order by rating desc");
     if (edb_nrows($result)) {
         $a = array();
         while ($row = edb_row($result)) {
             if (isset(ReviewForm::$rating_types[$row[0]])) {
                 $a[] = "<a href=\"" . hoturl("search", "q=re:me+rate:%22" . urlencode(ReviewForm::$rating_types[$row[0]]) . "%22") . "\" title='List rated reviews'>{$row['1']} &ldquo;" . htmlspecialchars(ReviewForm::$rating_types[$row[0]]) . "&rdquo; " . pluralx($row[1], "rating") . "</a>";
             }
         }
         if (count($a) > 0) {
             echo "<div class='hint g'>\nYour reviews have received ", commajoin($a);
             if (count($a) > 1) {
                 echo " (these sets might overlap)";
             }
             echo ".<a class='help' href='", hoturl("help", "t=revrate"), "' title='About ratings'>?</a></div>\n";
         }
     }
Пример #26
0
 private function _rows($field_list)
 {
     global $Conf;
     if (!$field_list) {
         return null;
     }
     // prepare query text
     $this->qopts["scores"] = array_keys($this->qopts["scores"]);
     if (empty($this->qopts["scores"])) {
         unset($this->qopts["scores"]);
     }
     $pq = $Conf->paperQuery($this->contact, $this->qopts);
     // make query
     $result = Dbl::qe_raw($pq);
     if (!$result) {
         return null;
     }
     // fetch rows
     $rows = array();
     while ($row = PaperInfo::fetch($result, $this->contact)) {
         $rows[$row->paperId] = $row;
     }
     // prepare review query (see also search > getfn == "reviewers")
     $this->review_list = array();
     if (isset($this->qopts["reviewList"]) && !empty($rows)) {
         $result = Dbl::qe("select Paper.paperId, reviewId, reviewType,\n                reviewSubmitted, reviewModified, reviewNeedsSubmit, reviewRound,\n                reviewOrdinal,\n                PaperReview.contactId, lastName, firstName, email\n                from Paper\n                join PaperReview using (paperId)\n                join ContactInfo on (PaperReview.contactId=ContactInfo.contactId)\n                where paperId?a", array_keys($rows));
         while ($row = edb_orow($result)) {
             Contact::set_sorter($row);
             $this->review_list[$row->paperId][] = $row;
         }
         foreach ($this->review_list as &$revlist) {
             usort($revlist, "PaperList::review_list_compar");
         }
         unset($revlist);
     }
     // prepare PC topic interests
     if (isset($this->qopts["allReviewerPreference"])) {
         $ord = 0;
         $pcm = pcMembers();
         foreach ($pcm as $pc) {
             $pc->prefOrdinal = sprintf("-0.%04d", $ord++);
             $pc->topicInterest = array();
         }
         $result = Dbl::qe("select contactId, topicId, " . $Conf->query_topic_interest() . " from TopicInterest");
         while ($row = edb_row($result)) {
             $pcm[$row[0]]->topicInterest[$row[1]] = $row[2];
         }
     }
     // analyze rows (usually noop)
     foreach ($field_list as $fdef) {
         $fdef->analyze($this, $rows);
     }
     // sort rows
     if (!empty($this->sorters)) {
         $rows = $this->_sort($rows);
         if (isset($this->qopts["allReviewScores"])) {
             $this->_sortReviewOrdinal($rows);
         }
     }
     // set `any->optID`
     if ($nopts = PaperOption::count_option_list()) {
         foreach ($rows as $prow) {
             foreach ($prow->options() as $o) {
                 if (!$this->any["opt{$o->id}"] && $this->contact->can_view_paper_option($prow, $o->option)) {
                     $this->any["opt{$o->id}"] = true;
                     --$nopts;
                 }
             }
             if (!$nopts) {
                 break;
             }
         }
     }
     Dbl::free($result);
     return $rows;
 }
Пример #27
0
function admin_home_messages()
{
    global $Opt, $Conf;
    $m = array();
    $errmarker = "<span class=\"error\">Error:</span> ";
    if (preg_match("/^(?:[1-4]\\.|5\\.[0123])/", phpversion())) {
        $m[] = $errmarker . "HotCRP requires PHP version 5.4 or higher.  You are running PHP version " . htmlspecialchars(phpversion()) . ".";
    }
    if (get_magic_quotes_gpc()) {
        $m[] = $errmarker . "The PHP <code>magic_quotes_gpc</code> feature is on, which is a bad idea.  Check that your Web server is using HotCRP’s <code>.htaccess</code> file.  You may also want to disable <code>magic_quotes_gpc</code> in your <code>php.ini</code> configuration file.";
    }
    if (get_magic_quotes_runtime()) {
        $m[] = $errmarker . "The PHP <code>magic_quotes_runtime</code> feature is on, which is a bad idea.  Check that your Web server is using HotCRP’s <code>.htaccess</code> file.  You may also want to disable <code>magic_quotes_runtime</code> in your <code>php.ini</code> configuration file.";
    }
    if (defined("JSON_HOTCRP")) {
        $m[] = "Your PHP was built without JSON functionality. HotCRP is using its built-in replacements; the native functions would be faster.";
    }
    if ((int) $Opt["globalSessionLifetime"] < $Opt["sessionLifetime"]) {
        $m[] = "PHP’s systemwide <code>session.gc_maxlifetime</code> setting, which is " . htmlspecialchars($Opt["globalSessionLifetime"]) . " seconds, is less than HotCRP’s preferred session expiration time, which is " . $Opt["sessionLifetime"] . " seconds.  You should update <code>session.gc_maxlifetime</code> in the <code>php.ini</code> file or users may be booted off the system earlier than you expect.";
    }
    if (!function_exists("imagecreate") && $Conf->setting("__gd_required")) {
        $m[] = $errmarker . "This PHP installation lacks support for the GD library, so HotCRP can’t generate backup score charts for old browsers. Some of your users require this backup. You should update your PHP installation. For example, on Ubuntu Linux, install the <code>php" . PHP_MAJOR_VERSION . "-gd</code> package.";
    }
    $result = Dbl::qx_raw("show variables like 'max_allowed_packet'");
    $max_file_size = ini_get_bytes("upload_max_filesize");
    if (($row = edb_row($result)) && $row[1] < $max_file_size && !get($Opt, "dbNoPapers")) {
        $m[] = $errmarker . "MySQL’s <code>max_allowed_packet</code> setting, which is " . htmlspecialchars($row[1]) . "&nbsp;bytes, is less than the PHP upload file limit, which is {$max_file_size}&nbsp;bytes.  You should update <code>max_allowed_packet</code> in the system-wide <code>my.cnf</code> file or the system may not be able to handle large papers.";
    }
    // Conference names
    if (get($Opt, "shortNameDefaulted")) {
        $m[] = "<a href=\"" . hoturl("settings", "group=basics") . "\">Set the conference abbreviation</a> to a short name for your conference, such as “OSDI ’14”.";
    } else {
        if (simplify_whitespace(Conf::$gShortName) != Conf::$gShortName) {
            $m[] = "The <a href=\"" . hoturl("settings", "group=basics") . "\">conference abbreviation</a> setting has a funny value. To fix it, remove leading and trailing spaces, use only space characters (no tabs or newlines), and make sure words are separated by single spaces (never two or more).";
        }
    }
    $site_contact = Contact::site_contact();
    if (!$site_contact->email || $site_contact->email == "*****@*****.**") {
        $m[] = "<a href=\"" . hoturl("settings", "group=basics") . "\">Set the conference contact’s name and email</a> so submitters can reach someone if things go wrong.";
    }
    // Any -100 preferences around?
    $result = Dbl::ql_raw($Conf->preferenceConflictQuery(false, "limit 1"));
    if ($row = edb_row($result)) {
        $m[] = "PC members have indicated paper conflicts (using review preferences of &#8722;100 or less) that aren’t yet confirmed. <a href='" . hoturl_post("autoassign", "a=prefconflict&amp;assign=1") . "' class='nw'>Confirm these conflicts</a>";
    }
    // Weird URLs?
    foreach (array("conferenceSite", "paperSite") as $k) {
        if (isset($Opt[$k]) && $Opt[$k] && !preg_match('`\\Ahttps?://(?:[-.~\\w:/?#\\[\\]@!$&\'()*+,;=]|%[0-9a-fA-F][0-9a-fA-F])*\\z`', $Opt[$k])) {
            $m[] = $errmarker . "The <code>\$Opt[\"{$k}\"]</code> setting, ‘<code>" . htmlspecialchars($Opt[$k]) . "</code>’, is not a valid URL.  Edit the <code>conf/options.php</code> file to fix this problem.";
        }
    }
    // Unnotified reviews?
    if ($Conf->setting("pcrev_assigntime", 0) > $Conf->setting("pcrev_informtime", 0)) {
        $assigntime = $Conf->setting("pcrev_assigntime");
        $result = Dbl::qe_raw("select paperId from PaperReview where reviewType>" . REVIEW_PC . " and timeRequested>timeRequestNotified and reviewSubmitted is null and reviewNeedsSubmit!=0 limit 1");
        if (edb_nrows($result)) {
            $m[] = "PC review assignments have changed.&nbsp; <a href=\"" . hoturl("mail", "template=newpcrev") . "\">Send review assignment notifications</a> <span class=\"barsep\">·</span> <a href=\"" . hoturl_post("index", "clearnewpcrev={$assigntime}") . "\">Clear this message</a>";
        } else {
            $Conf->save_setting("pcrev_informtime", $assigntime);
        }
    }
    // Review round expired?
    if (count($Conf->round_list()) > 1 && $Conf->time_review_open() && $Conf->missed_review_deadline($Conf->current_round(), true, false)) {
        $any_rounds_open = false;
        foreach ($Conf->defined_round_list() as $i => $rname) {
            if (!$any_rounds_open && !$Conf->missed_review_deadline($i, true, false) && $Conf->setting($Conf->review_deadline($i, true, false))) {
                $any_rounds_open = $rname;
            }
        }
        if ($any_rounds_open) {
            $m[] = "The deadline for the current review round, " . htmlspecialchars($Conf->current_round_name()) . ", has passed. You may want to <a href=\"" . hoturl("settings", "group=reviews") . "\">change the current round</a> to " . htmlspecialchars($any_rounds_open) . ".";
        }
    }
    if (count($m)) {
        $Conf->warnMsg('<div class="multimessage"><div>' . join('</div><div>', $m) . "</div></div>");
    }
}
Пример #28
0
 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);
                     }
                 }
             }
         }
     }
 }
 public function save($sv, $si)
 {
     global $Conf;
     if ($sv->update("review_form", json_encode($this->nrfj))) {
         $rf = ReviewForm::get();
         $scoreModified = array();
         foreach ($this->nrfj as $fid => $fj) {
             if (get($fj, "position") && get($fj, "options")) {
                 $result = Dbl::qe_raw("update PaperReview set {$fid}=0 where {$fid}>" . count($fj->options));
                 if ($result && $result->affected_rows > 0) {
                     $scoreModified[] = htmlspecialchars($fj->name);
                 }
                 Dbl::free($result);
             }
         }
         foreach ($rf->fmap as $fid => $f) {
             foreach (self::$setting_prefixes as $fx) {
                 unset($sv->req["{$fx}{$fid}"]);
             }
         }
         if (count($scoreModified)) {
             $sv->set_warning(null, "Your changes invalidated some existing review scores.  The invalid scores have been reset to “Unknown”.  The relevant fields were: " . join(", ", $scoreModified) . ".");
         }
         $Conf->invalidateCaches(array("rf" => true));
         // reset all word counts in case author visibility changed
         Dbl::qe("update PaperReview set reviewWordCount=null");
     }
 }