Beispiel #1
0
 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;
 }
Beispiel #5
0
 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);
         }
     }
 }
Beispiel #6
0
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);
         }
     }
 }
Beispiel #14
0
 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;
 }
Beispiel #15
0
}
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'>&nbsp;|&nbsp;</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 "&nbsp;", 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.');
Beispiel #16
0
        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 "&nbsp;", 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;
 }
Beispiel #21
0
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");
Beispiel #23
0
 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 &lt;angle brackets&gt;.";
                 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];
 }
Beispiel #25
0
 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);
 }
Beispiel #26
0
 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);
                     }
                 }
             }
         }
     }
 }
Beispiel #28
0
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();
 }