function __construct($source_tag, $dest_tag, $papersel, $strict, $header_title = null, $header_id = null)
 {
     global $Conf;
     $this->dest_tag = $dest_tag;
     $this->ordertype = $strict ? "aos" : "ao";
     $this->papersel = $papersel;
     $this->userrank = array();
     $this->info_printed = false;
     $this->header_title = $header_title;
     $this->header_id = $header_id;
     $this->starttime = time();
     // generate random order for paper comparisons
     if (count($papersel)) {
         $range = range(0, count($papersel) - 1);
         shuffle($range);
         $this->papershuffle = array_combine($papersel, $range);
     } else {
         $this->papershuffle = array();
     }
     // load current ranks: $userrank maps user => [rank, paper]
     $result = $Conf->qe("select paperId, tag, tagIndex from PaperTag where tag like '%~" . sqlq_for_like($source_tag) . "' and paperId in (" . join(",", $papersel) . ")");
     $len = strlen($source_tag) + 1;
     while ($row = edb_row($result)) {
         $l = (int) substr($row[1], 0, strlen($row[1]) - $len);
         $this->userrank[$l][] = array((int) $row[2], (int) $row[0]);
     }
     // sort $userrank[$user] by descending rank order
     foreach ($this->userrank as $user => &$ranks) {
         usort($ranks, array($this, "_comparUserrank"));
     }
     $this->rank = array();
     $this->currank = 0;
 }
 static function email_authored_papers($email, $reg)
 {
     $aupapers = array();
     $result = Dbl::q("select paperId, authorInformation from Paper where authorInformation like " . Dbl::utf8ci("'%\t" . sqlq_for_like($email) . "\t%'"));
     while ($row = PaperInfo::fetch($result, null)) {
         foreach ($row->author_list() as $au) {
             if (strcasecmp($au->email, $email) == 0) {
                 $aupapers[] = $row->paperId;
                 if ($reg && $au->firstName && !get($reg, "firstName")) {
                     $reg->firstName = $au->firstName;
                 }
                 if ($reg && $au->lastName && !get($reg, "lastName")) {
                     $reg->lastName = $au->lastName;
                 }
                 if ($reg && $au->affiliation && !get($reg, "affiliation")) {
                     $reg->affiliation = $au->affiliation;
                 }
             }
         }
     }
     return $aupapers;
 }
 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();
 }
        $numreviews = Dbl::fetch_ivalue("select count(*) from PaperReview where paperId={$prow->paperId} and reviewNeedsSubmit!=0");
        $Conf->update_papersub_setting(false);
        loadRows();
        // email contact authors themselves
        if (!$Me->privChair || defval($_REQUEST, "doemail") > 0) {
            HotCRPMailer::send_contacts($prow->conflictType >= CONFLICT_AUTHOR ? "@authorwithdraw" : "@adminwithdraw", $prow, array("reason" => $reason, "infoNames" => 1));
        }
        // email reviewers
        if ($numreviews > 0 && $Conf->time_review_open() || $prow->num_reviews_assigned() > 0) {
            HotCRPMailer::send_reviewers("@withdrawreviewer", $prow, array("reason" => $reason));
        }
        // remove voting tags so people don't have phantom votes
        if (TagInfo::has_vote()) {
            $q = array();
            foreach (TagInfo::vote_tags() as $t => $v) {
                $q[] = "tag='" . sqlq($t) . "' or tag like '%~" . sqlq_for_like($t) . "'";
            }
            Dbl::qe_raw("delete from PaperTag where paperId={$prow->paperId} and (" . join(" or ", $q) . ")");
        }
        $Me->log_activity("Withdrew", $prow->paperId);
        redirectSelf();
    } else {
        Conf::msg_error(whyNotText($whyNot, "withdraw"));
    }
}
if (isset($_REQUEST["revive"]) && !$newPaper && check_post()) {
    if (!($whyNot = $Me->perm_revive_paper($prow))) {
        Dbl::qe("update Paper set timeWithdrawn=0, timeSubmitted=if(timeSubmitted=-100,{$Now},0), withdrawReason=null where paperId={$prow->paperId}");
        $Conf->update_papersub_setting(true);
        loadRows();
        $Me->log_activity("Revived", $prow->paperId);
Beispiel #5
0
            $where[] = "action like " . Dbl::utf8ci("'% " . sqlq_for_like($row[1]) . "%'");
        }
    }
    if (count($where)) {
        $wheres[] = "(" . join(" or ", $where) . ")";
    } else {
        $Conf->infoMsg("No accounts match “" . htmlspecialchars($_REQUEST["acct"]) . "”.");
        $wheres[] = "false";
    }
}
if ($str = $_REQUEST["q"]) {
    $where = array();
    while (($str = ltrim($str)) != "") {
        preg_match('/^("[^"]+"?|[^"\\s]+)/s', $str, $m);
        $str = substr($str, strlen($m[0]));
        $where[] = "action like " . Dbl::utf8ci("'%" . sqlq_for_like($m[0]) . "%'");
    }
    $wheres[] = "(" . join(" or ", $where) . ")";
}
if (($count = cvtint(@$_REQUEST["n"])) <= 0) {
    Conf::msg_error("\"Show <i>n</i> records\" requires a number greater than 0.");
    $Eclass["n"] = " error";
    $count = $DEFAULT_COUNT;
}
$firstDate = false;
if ($_REQUEST["date"] == "") {
    $_REQUEST["date"] = "now";
}
if ($_REQUEST["date"] != "now" && isset($_REQUEST["search"])) {
    if (($firstDate = $Conf->parse_time($_REQUEST["date"])) === false) {
        Conf::msg_error("“" . htmlspecialchars($_REQUEST["date"]) . "” is not a valid date.");
function crpmerge_database($old_user, $new_user)
{
    global $Conf, $MergeError;
    // Now, scan through all the tables that possibly
    // specify a contactID and change it from their 2nd
    // contactID to their first contactId
    $oldid = $old_user->contactId;
    $newid = $new_user->contactId;
    $Conf->q("lock tables Paper write, ContactInfo write, PaperConflict write, ActionLog write, TopicInterest write, PaperComment write, PaperReview write, PaperReview as B write, PaperReviewPreference write, PaperReviewRefused write, ReviewRequest write, PaperWatch write, ReviewRating write");
    crpmergeone("Paper", "leadContactId", $oldid, $newid);
    crpmergeone("Paper", "shepherdContactId", $oldid, $newid);
    crpmergeone("Paper", "managerContactId", $oldid, $newid);
    // paper authorship
    $result = $Conf->qe("select paperId, authorInformation from Paper where authorInformation like " . Dbl::utf8ci("'%\t" . sqlq_for_like($old_user->email) . "\t%'"));
    $qs = array();
    while ($row = PaperInfo::fetch($result, null)) {
        foreach ($row->author_list() as $au) {
            if (strcasecmp($au->email, $old_user->email) == 0) {
                $au->email = $new_user->email;
            }
        }
        $qs[] = "update Paper set authorInformation='" . sqlq($row->parse_author_list()) . "' where paperId={$row->paperId}";
    }
    foreach ($qs as $q) {
        $Conf->qe($q);
    }
    // ensure uniqueness in PaperConflict
    $result = $Conf->qe("select paperId, conflictType from PaperConflict where contactId={$oldid}");
    $values = "";
    while ($row = edb_row($result)) {
        $values .= ", ({$row['0']}, {$newid}, {$row['1']})";
    }
    if ($values) {
        $Conf->qe("insert into PaperConflict (paperId, contactId, conflictType) values " . substr($values, 2) . " on duplicate key update conflictType=greatest(conflictType, values(conflictType))");
    }
    $Conf->qe("delete from PaperConflict where contactId={$oldid}");
    if (($old_user->roles | $new_user->roles) != $new_user->roles) {
        $new_user->roles |= $old_user->roles;
        $Conf->qe("update ContactInfo set roles={$new_user->roles} where contactId={$newid}");
    }
    crpmergeone("ActionLog", "contactId", $oldid, $newid);
    crpmergeoneignore("TopicInterest", "contactId", $oldid, $newid);
    crpmergeone("PaperComment", "contactId", $oldid, $newid);
    // archive duplicate reviews
    crpmergeoneignore("PaperReview", "contactId", $oldid, $newid);
    crpmergeone("PaperReview", "requestedBy", $oldid, $newid);
    crpmergeoneignore("PaperReviewPreference", "contactId", $oldid, $newid);
    crpmergeone("PaperReviewRefused", "contactId", $oldid, $newid);
    crpmergeone("PaperReviewRefused", "requestedBy", $oldid, $newid);
    crpmergeone("ReviewRequest", "requestedBy", $oldid, $newid);
    crpmergeoneignore("PaperWatch", "contactId", $oldid, $newid);
    crpmergeoneignore("ReviewRating", "contactId", $oldid, $newid);
    // Remove the old contact record
    if ($MergeError == "") {
        if (!$Conf->q("delete from ContactInfo where contactId={$oldid}")) {
            $MergeError .= $Conf->db_error_html(true);
        }
    }
    $Conf->qe("unlock tables");
    // Update PC settings if we need to
    if ($old_user->isPC) {
        $Conf->invalidateCaches(array("pc" => 1));
    }
}
 function query($paper_sensitive)
 {
     global $Conf;
     $cols = array();
     $where = array("email not regexp '^anonymous[0-9]*\$'");
     $joins = array("ContactInfo");
     // paper limit
     if ($this->need_papers() && isset($this->papersel)) {
         $where[] = "Paper.paperId in (" . join(",", $this->papersel) . ")";
     }
     // paper type limit
     if ($this->type == "s") {
         $where[] = "Paper.timeSubmitted>0";
     } else {
         if ($this->type == "unsub") {
             $where[] = "Paper.timeSubmitted<=0 and Paper.timeWithdrawn<=0";
         } else {
             if ($this->type == "dec:any") {
                 $where[] = "Paper.timeSubmitted>0 and Paper.outcome!=0";
             } else {
                 if ($this->type == "dec:none") {
                     $where[] = "Paper.timeSubmitted>0 and Paper.outcome=0";
                 } else {
                     if ($this->type == "dec:yes") {
                         $where[] = "Paper.timeSubmitted>0 and Paper.outcome>0";
                     } else {
                         if ($this->type == "dec:no") {
                             $where[] = "Paper.timeSubmitted>0 and Paper.outcome<0";
                         } else {
                             if (substr($this->type, 0, 4) == "dec:") {
                                 $nw = count($where);
                                 foreach ($Conf->decision_map() as $dnum => $dname) {
                                     if (strcasecmp($dname, substr($this->type, 4)) == 0) {
                                         $where[] = "Paper.timeSubmitted>0 and Paper.outcome={$dnum}";
                                         break;
                                     }
                                 }
                                 if (count($where) == $nw) {
                                     return false;
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     // additional manager limit
     if (!isset($this->sel_nonmanager[$this->type]) && !$this->contact->privChair) {
         $where[] = "Paper.managerContactId=" . $this->contact->contactId;
     }
     // reviewer limit
     if (!preg_match('_\\A(new|unc|c|allc|)(pc|ext|myext|)rev\\z_', $this->type, $revmatch)) {
         $revmatch = false;
     }
     // build query
     if ($this->type == "all") {
         $needpaper = $needconflict = $needreview = false;
     } else {
         if ($this->type == "pc" || substr($this->type, 0, 3) == "pc:") {
             $needpaper = $needconflict = $needreview = false;
             $where[] = "(ContactInfo.roles&" . Contact::ROLE_PC . ")!=0";
             if ($this->type != "pc") {
                 $where[] = "ContactInfo.contactTags like " . Dbl::utf8ci("'% " . sqlq_for_like(substr($this->type, 3)) . "#%'");
             }
         } else {
             if ($revmatch) {
                 $needpaper = $needreview = true;
                 $needconflict = false;
                 $joins[] = "join Paper";
                 $joins[] = "join PaperReview on (PaperReview.paperId=Paper.paperId and PaperReview.contactId=ContactInfo.contactId)";
                 $where[] = "Paper.paperId=PaperReview.paperId";
             } else {
                 if ($this->type == "lead" || $this->type == "shepherd") {
                     $needpaper = $needconflict = $needreview = true;
                     $joins[] = "join Paper on (Paper.{$this->type}ContactId=ContactInfo.contactId)";
                     $joins[] = "left join PaperReview on (PaperReview.paperId=Paper.paperId and PaperReview.contactId=ContactInfo.contactId)";
                 } else {
                     $needpaper = $needconflict = true;
                     $needreview = false;
                     if ($Conf->au_seerev == Conf::AUSEEREV_UNLESSINCOMPLETE) {
                         $cols[] = "(coalesce(allr.contactId,0)!=0) has_review";
                         $cols[] = "coalesce(allr.has_outstanding_review,0) has_outstanding_review";
                         $joins[] = "left join (select contactId, max(if(reviewNeedsSubmit!=0 and timeSubmitted>0,1,0)) has_outstanding_review from PaperReview join Paper on (Paper.paperId=PaperReview.paperId) group by PaperReview.contactId) as allr using (contactId)";
                     }
                     $joins[] = "join Paper";
                     $where[] = "PaperConflict.conflictType>=" . CONFLICT_AUTHOR;
                     if ($Conf->au_seerev == Conf::AUSEEREV_TAGS) {
                         $joins[] = "left join (select paperId, group_concat(' ', tag, '#', tagIndex order by tag separator '') as paperTags from PaperTag group by paperId) as PaperTags on (PaperTags.paperId=Paper.paperId)";
                         $cols[] = "PaperTags.paperTags";
                     }
                 }
             }
         }
     }
     // reviewer match
     if ($revmatch) {
         // Submission status
         if ($revmatch[1] == "c") {
             $where[] = "PaperReview.reviewSubmitted>0";
         } else {
             if ($revmatch[1] == "unc" || $revmatch[1] == "new") {
                 $where[] = "PaperReview.reviewSubmitted is null and PaperReview.reviewNeedsSubmit!=0 and Paper.timeSubmitted>0";
             }
         }
         if ($revmatch[1] == "new") {
             $where[] = "PaperReview.timeRequested>PaperReview.timeRequestNotified";
         }
         if ($revmatch[1] == "allc") {
             $joins[] = "left join (select contactId, max(if(reviewNeedsSubmit!=0 and timeSubmitted>0,1,0)) anyReviewNeedsSubmit from PaperReview join Paper on (Paper.paperId=PaperReview.paperId) group by contactId) AllReviews on (AllReviews.contactId=ContactInfo.contactId)";
             $where[] = "AllReviews.anyReviewNeedsSubmit=0";
         }
         if ($this->newrev_since) {
             $where[] = "PaperReview.timeRequested>={$this->newrev_since}";
         }
         // Withdrawn papers may not count
         if ($revmatch[1] == "") {
             $where[] = "(Paper.timeSubmitted>0 or PaperReview.reviewSubmitted>0)";
         }
         // Review type
         if ($revmatch[2] == "ext" || $revmatch[2] == "myext") {
             $where[] = "PaperReview.reviewType=" . REVIEW_EXTERNAL;
         } else {
             if ($revmatch[2] == "pc") {
                 $where[] = "PaperReview.reviewType>" . REVIEW_EXTERNAL;
             }
         }
         if ($revmatch[2] == "myext") {
             $where[] = "PaperReview.requestedBy=" . $this->contact->contactId;
         }
     }
     // query construction
     $q = "select ContactInfo.contactId, firstName, lastName, email,\n            password, roles, contactTags, preferredEmail, " . ($needconflict ? "PaperConflict.conflictType" : "0 as conflictType");
     if ($needpaper) {
         $q .= ", Paper.paperId, Paper.title, Paper.abstract,\n                Paper.authorInformation, Paper.outcome, Paper.blind,\n                Paper.timeSubmitted, Paper.timeWithdrawn,\n                Paper.shepherdContactId, Paper.capVersion,\n                Paper.managerContactId";
     } else {
         $q .= ", -1 as paperId";
     }
     if ($needreview) {
         $q .= ", PaperReview.reviewType, PaperReview.reviewType as myReviewType";
     }
     if ($needconflict) {
         $joins[] = "left join PaperConflict on (PaperConflict.paperId=Paper.paperId and PaperConflict.contactId=ContactInfo.contactId)";
     }
     $q .= "\nfrom " . join("\n", $joins) . "\nwhere " . join("\n    and ", $where) . "\norder by ";
     if (!$needpaper) {
         $q .= "email";
     } else {
         if ($paper_sensitive) {
             $q .= "Paper.paperId, email";
         } else {
             $q .= "email, Paper.paperId";
         }
     }
     return $q;
 }
function do_tags()
{
    global $Conf, $Me, $papersel;
    // check tags
    $tagger = new Tagger($Me);
    $t1 = array();
    $errors = array();
    foreach (preg_split('/[\\s,]+/', (string) @$_REQUEST["tag"]) as $t) {
        if ($t === "") {
            /* nada */
        } else {
            if (!($t = $tagger->check($t, Tagger::NOPRIVATE))) {
                $errors[] = $tagger->error_html;
            } else {
                if (TagInfo::base($t) === "pc") {
                    $errors[] = "The “pc” user tag is set automatically for all PC members.";
                } else {
                    $t1[] = $t;
                }
            }
        }
    }
    if (count($errors)) {
        return Conf::msg_error(join("<br>", $errors));
    } else {
        if (!count($t1)) {
            return $Conf->warnMsg("Nothing to do.");
        }
    }
    // modify database
    Dbl::qe("lock tables ContactInfo write");
    Conf::$no_invalidate_caches = true;
    $users = array();
    if ($_REQUEST["tagtype"] === "s") {
        // erase existing tags
        $likes = array();
        $removes = array();
        foreach ($t1 as $t) {
            list($tag, $index) = TagInfo::split_index($t);
            $removes[] = $t;
            $likes[] = "contactTags like " . Dbl::utf8ci("'% " . sqlq_for_like($tag) . "#%'");
        }
        foreach (Dbl::fetch_first_columns(Dbl::qe("select contactId from ContactInfo where " . join(" or ", $likes))) as $cid) {
            $users[(int) $cid] = (object) array("id" => (int) $cid, "add_tags" => [], "remove_tags" => $removes);
        }
    }
    // account for request
    $key = $_REQUEST["tagtype"] === "d" ? "remove_tags" : "add_tags";
    foreach ($papersel as $cid) {
        if (!isset($users[(int) $cid])) {
            $users[(int) $cid] = (object) array("id" => (int) $cid, "add_tags" => [], "remove_tags" => []);
        }
        $users[(int) $cid]->{$key} = array_merge($users[(int) $cid]->{$key}, $t1);
    }
    // apply modifications
    foreach ($users as $cid => $cj) {
        $us = new UserStatus(array("send_email" => false));
        if (!$us->save($cj)) {
            $errors = array_merge($errors, $us->error_messages());
        }
    }
    Dbl::qe("unlock tables");
    Conf::$no_invalidate_caches = false;
    $Conf->invalidateCaches(["pc" => true]);
    // report
    if (!count($errors)) {
        $Conf->confirmMsg("Tags saved.");
        redirectSelf(array("tagact" => null, "tag" => null));
    } else {
        Conf::msg_error(join("<br>", $errors));
    }
}
 private function _search_one_tag($value, $old_arg)
 {
     if (($starpos = strpos($value, "*")) !== false) {
         $arg = "( like '" . str_replace("*", "%", sqlq_for_like($value)) . "'";
         if ($starpos == 0) {
             $arg .= " and  not like '%~%'";
         }
         $arg .= ")";
     } else {
         if ($value === "any" || $value === "none") {
             $arg = "( is not null and ( not like '%~%' or  like '{$this->cid}~%'" . ($this->privChair ? " or  like '~~%'" : "") . "))";
         } else {
             $arg = "='" . sqlq($value) . "'";
         }
     }
     return $old_arg ? "{$old_arg} or {$arg}" : $arg;
 }
Beispiel #10
0
 private static function format_query_args($dblink, $qstr, $argv)
 {
     $original_qstr = $qstr;
     $strpos = $argpos = 0;
     $usedargs = array();
     while (($strpos = strpos($qstr, "?", $strpos)) !== false) {
         // argument name
         $nextpos = $strpos + 1;
         $nextch = substr($qstr, $nextpos, 1);
         if ($nextch === "?") {
             $qstr = substr($qstr, 0, $strpos + 1) . substr($qstr, $strpos + 2);
             $strpos = $strpos + 1;
             continue;
         } else {
             if ($nextch === "{" && ($rbracepos = strpos($qstr, "}", $nextpos + 1)) !== false) {
                 $thisarg = substr($qstr, $nextpos + 1, $rbracepos - $nextpos - 1);
                 if ($thisarg === (string) (int) $thisarg) {
                     --$thisarg;
                 }
                 $nextpos = $rbracepos + 1;
                 $nextch = substr($qstr, $nextpos, 1);
             } else {
                 while (get($usedargs, $argpos)) {
                     ++$argpos;
                 }
                 $thisarg = $argpos;
             }
         }
         if (!array_key_exists($thisarg, $argv)) {
             trigger_error(self::landmark() . ": query '{$original_qstr}' argument " . (is_int($thisarg) ? $thisarg + 1 : $thisarg) . " not set");
         }
         $usedargs[$thisarg] = true;
         // argument format
         $arg = get($argv, $thisarg);
         if ($nextch === "e" || $nextch === "E") {
             if ($arg === null) {
                 $arg = $nextch === "e" ? " IS NULL" : " IS NOT NULL";
             } else {
                 if (is_int($arg) || is_float($arg)) {
                     $arg = ($nextch === "e" ? "=" : "!=") . $arg;
                 } else {
                     $arg = ($nextch === "e" ? "='" : "!='") . $dblink->real_escape_string($arg) . "'";
                 }
             }
             ++$nextpos;
         } else {
             if ($nextch === "a" || $nextch === "A") {
                 if ($arg === null) {
                     $arg = array();
                 } else {
                     if (is_int($arg) || is_float($arg) || is_string($arg)) {
                         $arg = array($arg);
                     }
                 }
                 foreach ($arg as $x) {
                     if (!is_int($x) && !is_float($x)) {
                         reset($arg);
                         foreach ($arg as &$y) {
                             $y = "'" . $dblink->real_escape_string($y) . "'";
                         }
                         unset($y);
                         break;
                     }
                 }
                 if (empty($arg)) {
                     // We want `foo IN ()` and `foo NOT IN ()`.
                     // That is, we want `false` and `true`. We compromise. The
                     // statement `foo=NULL` is always NULL -- which is falsy
                     // -- even if `foo IS NULL`. The statement `foo IS NOT
                     // NULL` is true unless `foo IS NULL`.
                     $arg = $nextch === "a" ? "=NULL" : " IS NOT NULL";
                 } else {
                     if (count($arg) === 1) {
                         reset($arg);
                         $arg = ($nextch === "a" ? "=" : "!=") . current($arg);
                     } else {
                         $arg = ($nextch === "a" ? " IN (" : " NOT IN (") . join(", ", $arg) . ")";
                     }
                 }
                 ++$nextpos;
             } else {
                 if ($nextch === "s") {
                     $arg = $dblink->real_escape_string($arg);
                     ++$nextpos;
                 } else {
                     if ($nextch === "l") {
                         $arg = sqlq_for_like($arg);
                         ++$nextpos;
                         if (substr($qstr, $nextpos + 1, 1) === "s") {
                             ++$nextpos;
                         } else {
                             $arg = "'" . $arg . "'";
                         }
                     } else {
                         if ($nextch === "v") {
                             ++$nextpos;
                             if (!is_array($arg) || empty($arg)) {
                                 trigger_error(self::landmark() . ": query '{$original_qstr}' argument " . (is_int($thisarg) ? $thisarg + 1 : $thisarg) . " should be nonempty array");
                                 $arg = "NULL";
                             } else {
                                 $alln = -1;
                                 $vs = [];
                                 foreach ($arg as $x) {
                                     if (!is_array($x)) {
                                         $x = [$x];
                                     }
                                     $n = count($x);
                                     if ($alln === -1) {
                                         $alln = $n;
                                     }
                                     if ($alln !== $n && $alln !== -2) {
                                         trigger_error(self::landmark() . ": query '{$original_qstr}' argument " . (is_int($thisarg) ? $thisarg + 1 : $thisarg) . " has components of different lengths");
                                         $alln = -2;
                                     }
                                     foreach ($x as &$y) {
                                         if ($y === null) {
                                             $y = "NULL";
                                         } else {
                                             if (!is_int($y) && !is_float($y)) {
                                                 $y = "'" . $dblink->real_escape_string($y) . "'";
                                             }
                                         }
                                     }
                                     unset($y);
                                     $vs[] = "(" . join(",", $x) . ")";
                                 }
                                 $arg = join(", ", $vs);
                             }
                         } else {
                             if ($arg === null) {
                                 $arg = "NULL";
                             } else {
                                 if (!is_int($arg) && !is_float($arg)) {
                                     $arg = "'" . $dblink->real_escape_string($arg) . "'";
                                 }
                             }
                         }
                     }
                 }
             }
         }
         // combine
         $suffix = substr($qstr, $nextpos);
         $qstr = substr($qstr, 0, $strpos) . $arg . $suffix;
         $strpos = strlen($qstr) - strlen($suffix);
     }
     return $qstr;
 }
 function rows($listname)
 {
     // PC tags
     $queryOptions = array();
     if (str_starts_with($listname, "#")) {
         $queryOptions["where"] = "(u.contactTags like " . Dbl::utf8ci("'% " . sqlq_for_like(substr($listname, 1)) . "#%'") . ")";
         $listname = "pc";
     }
     // get paper list
     if (!($baseFieldId = $this->listFields($listname))) {
         Conf::msg_error("There is no people list query named “" . htmlspecialchars($listname) . "”.");
         return null;
     }
     $this->limit = array_shift($baseFieldId);
     // run query
     return $this->_rows($queryOptions);
 }